//#define RemoteAvatarBundles using System; using System.Collections.Generic; using System.IO; using UnityEngine; namespace Passer.Humanoid { using Pawn; /// /// Interface for Humanoid Networking functions /// public interface IHumanoidNetworking { void Send(bool b); void Send(byte b); void Send(int x); void Send(float f); void Send(Vector3 v); void Send(Quaternion q); bool ReceiveBool(); byte ReceiveByte(); int ReceiveInt(); float ReceiveFloat(); Vector3 ReceiveVector3(); Quaternion ReceiveQuaternion(); float sendRate { get; } HumanoidNetworking.DebugLevel debug { get; } HumanoidNetworking.Smoothing smoothing { get; } bool createLocalRemotes { get; set; } bool isLocal { get; } ulong nwId { get; } bool syncFingerSwing { get; } bool syncTracking { get; set; } bool fuseTracking { get; } List humanoids { get; } HumanoidNetworking.HumanoidPose lastHumanoidPose { get; set; } ulong GetObjectIdentity(GameObject obj); GameObject GetGameObject(ulong objIdentity); void InstantiateHumanoid(HumanoidControl humanoid); void DestroyHumanoid(HumanoidControl humanoid); void UpdateHumanoidPose(HumanoidControl humanoid); void Grab(HandTarget handTarget, GameObject obj, bool rangeCheck, HandTarget.GrabType grabType = HandTarget.GrabType.HandGrab); void LetGo(HandTarget handTarget); void ChangeAvatar(HumanoidControl humanoid, string remoteAvatarName, string possessionLocation = null); void SyncTrackingSpace(HumanoidControl humanoid); void DebugLog(string s); void DebugWarning(string s); void DebugError(string s); void ReenableNetworkSync(GameObject obj); void DisableNetworkSync(GameObject obj); } /// /// Humanoid Networking /// public static class HumanoidNetworking { //public enum Smoothing { // None, // Interpolation, // Extrapolation //}; public enum Smoothing { None, Interpolation, Extrapolation }; public enum DebugLevel { Debug, Info, Warning, Error, None, } public static DebugLevel debug = DebugLevel.Error; public class IMessage #if hNW_MIRROR : Mirror.NetworkMessage #endif { public IMessage() { } public IMessage(byte[] data) { Deserialize(data); } public virtual byte[] Serialize() { return null; } public virtual void Deserialize(byte[] data) { } // This is the same code as in the base class IMessage // It is needed because the base class implementation only // results in compiler errors. #if hNW_MIRROR //public override void Serialize(Mirror.NetworkWriter writer) { // byte[] data = Serialize(); // //writer.WriteBytesAndSize(data); // Mirror.NetworkWriterExtensions.WriteBytesAndSize(writer, data); //} //public override void Deserialize(Mirror.NetworkReader reader) { // //byte[] data = reader.ReadBytesAndSize(); // byte[] data = Mirror.NetworkReaderExtensions.ReadBytesAndSize(reader); // Deserialize(data); //} #endif } public static void Connected(HumanoidControl humanoid) { OnConnectedToNetwork?.Invoke(humanoid); } #region Instantiate Humanoid public class InstantiateHumanoid : IMessage { public ulong nwId; public byte humanoidId; public string name; public string avatarPrefabName; public bool physics; public string possessionLocation = ""; #if RemoteAvatarBundles public int remoteAvatarBundleSize; public byte[] remoteAvatarBundle; #endif public InstantiateHumanoid() { } public InstantiateHumanoid(HumanoidControl humanoid) { nwId = humanoid.nwId; humanoidId = (byte)humanoid.humanoidId; int nameLength = humanoid.gameObject.name.Length; if (nameLength > 9 && humanoid.gameObject.name.Substring(nameLength - 9, 9) == " (Remote)") name = humanoid.gameObject.name.Substring(0, nameLength - 9); else name = humanoid.gameObject.name; if (humanoid.remoteAvatar == null) { Possessable avatarPossessable = humanoid.avatarRig.GetComponent(); if (avatarPossessable != null) { possessionLocation = avatarPossessable.siteLocation; avatarPrefabName = avatarPossessable.assetPath; } else avatarPrefabName = humanoid.avatarRig.name; // .Substring(0, humanoid.avatarRig.name.Length - 7); } else { Possessable avatarPossessable = humanoid.remoteAvatar.GetComponent(); if (avatarPossessable != null) { possessionLocation = avatarPossessable.siteLocation; avatarPrefabName = avatarPossessable.assetPath; } else avatarPrefabName = humanoid.remoteAvatar.name; } #if RemoteAvatarBundles /* Get the remoteAvatarBundle from the streaming assets */ remoteAvatarBundle = File.ReadAllBytes(Application.streamingAssetsPath + "/RemoteAvatarBundles/" + avatarPrefabName); remoteAvatarBundleSize = remoteAvatarBundle.Length; #endif physics = humanoid.physics; } public InstantiateHumanoid(byte[] data) : base(data) { } public override byte[] Serialize() { MemoryStream ms = new MemoryStream(); BinaryWriter bw = new BinaryWriter(ms); bw.Write(nwId); bw.Write(humanoidId); bw.Write(name); bw.Write(avatarPrefabName); bw.Write(physics); if (possessionLocation == null) possessionLocation = ""; bw.Write(possessionLocation); #if RemoteAvatarBundles bw.Write(remoteAvatarBundleSize); bw.Write(remoteAvatarBundle); #endif byte[] data = ms.ToArray(); return data; } public override void Deserialize(byte[] data) { MemoryStream ms = new MemoryStream(data); BinaryReader br = new BinaryReader(ms); nwId = br.ReadUInt64(); humanoidId = br.ReadByte(); name = br.ReadString(); avatarPrefabName = br.ReadString(); physics = br.ReadBoolean(); possessionLocation = br.ReadString(); if (possessionLocation == "") possessionLocation = null; #if RemoteAvatarBundles remoteAvatarBundleSize = br.ReadInt32(); remoteAvatarBundle = br.ReadBytes(remoteAvatarBundleSize); #endif } // This is the same code as in the base class IMessage // It is needed because the base class implementation only // results in compiler errors. #if hNW_MIRROR //public override void Serialize(Mirror.NetworkWriter writer) { // byte[] data = Serialize(); // Mirror.NetworkWriterExtensions.WriteBytesAndSize(writer, data); //} //public override void Deserialize(Mirror.NetworkReader reader) { // byte[] data = Mirror.NetworkReaderExtensions.ReadBytesAndSize(reader); // Deserialize(data); //} #endif } public static void ReceiveInstantiate(this IHumanoidNetworking networking, byte[] serializedData) { InstantiateHumanoid data = new InstantiateHumanoid(serializedData); Receive(networking, data); } public static void Receive(this IHumanoidNetworking receivingNetworking, InstantiateHumanoid msg) { GameObject networkingObj = receivingNetworking.GetGameObject(msg.nwId); if (networkingObj == null) { if (receivingNetworking.debug <= DebugLevel.Error) receivingNetworking.DebugLog("Could not find Networking for Instantiate Humanoid " + msg.nwId + "/" + msg.humanoidId); return; } IHumanoidNetworking networking = networkingObj.GetComponent(); if (networking.isLocal && !networking.createLocalRemotes) { if (networking.debug <= DebugLevel.Debug) networking.DebugLog("Remote Humanoid " + msg.nwId + "/" + msg.humanoidId + " is local and local remotes are not created"); return; } HumanoidControl remoteHumanoid = FindRemoteHumanoid(networking.humanoids, msg.humanoidId); if (remoteHumanoid != null) { if (networking.debug <= DebugLevel.Warning) networking.DebugLog("Remote Humanoid " + msg.nwId + "/" + msg.humanoidId + " already exists"); // This remote humanoid already exists return; } if (networking.debug <= DebugLevel.Info) networking.DebugLog("Receive Instantiate Humanoid " + msg.nwId + "/" + msg.humanoidId); remoteHumanoid = InstantiateRemoteHumanoid(msg.name, Vector3.zero, Quaternion.identity); //, position, rotation); remoteHumanoid.nwId = msg.nwId; remoteHumanoid.humanoidId = msg.humanoidId; if (networking.debug <= DebugLevel.Info) networking.DebugLog("Remote Humanoid " + remoteHumanoid.nwId + "/" + remoteHumanoid.humanoidId + " Added"); #if RemoteAvatarBundles AssetBundle assetBundle = AssetBundle.LoadFromMemory(data.remoteAvatarBundle); GameObject remoteAvatar = assetBundle.LoadAsset(data.avatarPrefabName); if (remoteAvatar == null) { if (networking.debug <= PawnNetworking.DebugLevel.Error) networking.DebugError("Could not load remote avatar " + data.avatarPrefabName + ". Asset Bundle is not present"); return; } #else if (msg.possessionLocation != null) { Debug.Log("Need to download instantiate avatar possession from " + msg.possessionLocation); remoteHumanoid.physics = msg.physics; HumanoidPlayer.instance.StartCoroutine( VisitorPossessions.RetrievePossessableAsync(msg.possessionLocation, msg.avatarPrefabName, retrievedAvatar => InstantiateRetrievedAvatar(networking, remoteHumanoid, retrievedAvatar)) ); return; } GameObject remoteAvatar = (GameObject)Resources.Load(msg.avatarPrefabName); if (remoteAvatar == null) { if (networking.debug <= DebugLevel.Error) networking.DebugError("Could not load remote avatar " + msg.avatarPrefabName + ". Is it located in a Resources folder?"); return; } #endif remoteHumanoid.physics = msg.physics; remoteHumanoid.LocalChangeAvatar(remoteAvatar); networking.humanoids.Add(remoteHumanoid); if (OnNewRemoteHumanoid != null) OnNewRemoteHumanoid(remoteHumanoid); } private static void InstantiateRetrievedAvatar(IHumanoidNetworking networking, HumanoidControl remoteHumanoid, GameObject retrievedAvatar) { if (retrievedAvatar == null) { if (networking.debug <= DebugLevel.Error) Debug.LogError("Could not retrieve avatar."); //return; } else { if (networking.debug <= DebugLevel.Info) networking.DebugLog("Receive Change Possessesable Avatar " + retrievedAvatar); remoteHumanoid.LocalChangeAvatar(retrievedAvatar); } networking.humanoids.Add(remoteHumanoid); if (OnNewRemoteHumanoid != null) OnNewRemoteHumanoid(remoteHumanoid); } public static HumanoidControl FindRemoteHumanoid(List humanoids, ulong nwId, int humanoidId) { foreach (HumanoidControl humanoid in humanoids) { if (humanoid.isRemote && humanoid.nwId == nwId && humanoid.humanoidId == humanoidId) return humanoid; } return null; } private static HumanoidControl InstantiateRemoteHumanoid(string name, Vector3 position, Quaternion rotation) { GameObject remoteHumanoidPrefab = (GameObject)Resources.Load("RemoteHumanoid"); GameObject remoteHumanoidObj = UnityEngine.Object.Instantiate(remoteHumanoidPrefab, position, rotation); remoteHumanoidObj.name = name + " (Remote)"; HumanoidControl remoteHumanoid = remoteHumanoidObj.GetComponent(); remoteHumanoid.isRemote = true; return remoteHumanoid; } public delegate void ConnectedToNetwork(HumanoidControl humanoid); public static event ConnectedToNetwork OnConnectedToNetwork; public delegate void NewRemoteHumanoidArgs(HumanoidControl humanoid); public static event NewRemoteHumanoidArgs OnNewRemoteHumanoid; #endregion #region Destroy Humanoid public class DestroyHumanoid : IMessage { public ulong nwId; public byte humanoidId; public DestroyHumanoid() { } public DestroyHumanoid(HumanoidControl humanoid) { nwId = humanoid.nwId; humanoidId = (byte)humanoid.humanoidId; } public DestroyHumanoid(byte[] data) : base(data) { } public override byte[] Serialize() { MemoryStream ms = new MemoryStream(); BinaryWriter bw = new BinaryWriter(ms); bw.Write(nwId); bw.Write(humanoidId); byte[] data = ms.ToArray(); return data; } public override void Deserialize(byte[] data) { MemoryStream ms = new MemoryStream(data); BinaryReader br = new BinaryReader(ms); nwId = br.ReadUInt64(); humanoidId = br.ReadByte(); } // This is the same code as in the base class IMessage // It is needed because the base class implementation only // results in compiler errors. #if hNW_MIRROR //public override void Serialize(Mirror.NetworkWriter writer) { // byte[] data = Serialize(); // Mirror.NetworkWriterExtensions.WriteBytesAndSize(writer, data); //} //public override void Deserialize(Mirror.NetworkReader reader) { // byte[] data = Mirror.NetworkReaderExtensions.ReadBytesAndSize(reader); // Deserialize(data); //} #endif } public static void ReceiveDestroy(this IHumanoidNetworking networking, byte[] serializedData) { DestroyHumanoid data = new DestroyHumanoid(serializedData); Receive(networking, data); } public static void Receive(this IHumanoidNetworking networking, DestroyHumanoid msg) { if (networking.isLocal && !networking.createLocalRemotes) return; HumanoidControl remoteHumanoid = FindRemoteHumanoid(networking.humanoids, msg.humanoidId); if (remoteHumanoid == null) { // Unknown remote humanoid return; } if (remoteHumanoid.gameObject != null) UnityEngine.Object.Destroy(remoteHumanoid.gameObject); } #endregion #region Pose [Serializable] public struct Vector3S { public float x; public float y; public float z; public Vector3S(Vector3 v) { x = v.x; y = v.y; z = v.z; } public Vector3S(Quaternion q) { Vector3 euler = q.eulerAngles; x = euler.x; y = euler.y; z = euler.z; } public Vector3 vector3 { get { return new Vector3(x, y, z); } } public Quaternion quaternion { get { return Quaternion.Euler(x, y, z); } } public void Write(BinaryWriter bw) { bw.Write(x); bw.Write(y); bw.Write(z); } public static Vector3S Read(BinaryReader br) { float x = br.ReadSingle(); float y = br.ReadSingle(); float z = br.ReadSingle(); Vector3S v = new Vector3S() { x = x, y = y, z = z }; return v; } } // Rotations can be sent much more efficient: 3 * 16bit (angles) or 4 * 16bit (quatornions) [Serializable] public class HumanoidAnimatorParameters { public class AnimatorParameter { public AnimatorControllerParameterType type; public bool boolValue; public float floatValue; public int intValue; } public AnimatorParameter[] parameters; public HumanoidAnimatorParameters() { parameters = new AnimatorParameter[0]; } public HumanoidAnimatorParameters(HumanoidControl humanoid) { if (humanoid.targetsRig.runtimeAnimatorController == null) { parameters = new AnimatorParameter[0]; return; } parameters = new AnimatorParameter[humanoid.targetsRig.parameterCount]; for (int i = 0; i < humanoid.targetsRig.parameterCount; i++) { AnimatorControllerParameter parameter = humanoid.targetsRig.parameters[i]; parameters[i] = new AnimatorParameter() { type = parameter.type }; switch (parameters[i].type) { case AnimatorControllerParameterType.Bool: parameters[i].boolValue = humanoid.targetsRig.GetBool(parameter.name); break; case AnimatorControllerParameterType.Float: parameters[i].floatValue = humanoid.targetsRig.GetFloat(parameter.name); break; case AnimatorControllerParameterType.Int: parameters[i].intValue = humanoid.targetsRig.GetInteger(parameter.name); break; case AnimatorControllerParameterType.Trigger: parameters[i].boolValue = humanoid.targetsRig.GetBool(parameter.name); break; } } } public virtual void Write(BinaryWriter bw) { bw.Write(parameters.Length); for (int i = 0; i < parameters.Length; i++) { bw.Write((byte)parameters[i].type); switch (parameters[i].type) { case AnimatorControllerParameterType.Bool: bw.Write(parameters[i].boolValue); break; case AnimatorControllerParameterType.Float: bw.Write(parameters[i].floatValue); break; case AnimatorControllerParameterType.Int: bw.Write(parameters[i].intValue); break; case AnimatorControllerParameterType.Trigger: bw.Write(parameters[i].boolValue); break; } } } public static HumanoidAnimatorParameters Read(BinaryReader br) { int parameterCount = br.ReadInt32(); HumanoidAnimatorParameters animatorParameters = new HumanoidAnimatorParameters() { parameters = new AnimatorParameter[parameterCount] }; for (int i = 0; i < parameterCount; i++) { AnimatorControllerParameterType parameterType = (AnimatorControllerParameterType)br.ReadByte(); animatorParameters.parameters[i] = new AnimatorParameter() { type = parameterType }; switch (parameterType) { case AnimatorControllerParameterType.Bool: animatorParameters.parameters[i].boolValue = br.ReadBoolean(); break; case AnimatorControllerParameterType.Float: animatorParameters.parameters[i].floatValue = br.ReadSingle(); break; case AnimatorControllerParameterType.Int: animatorParameters.parameters[i].intValue = br.ReadInt32(); break; case AnimatorControllerParameterType.Trigger: animatorParameters.parameters[i].boolValue = br.ReadBoolean(); break; } } return animatorParameters; } } [Serializable] public class HumanoidHandPose : HumanoidTargetPose { public float thumbCurl; public float indexCurl; public float middleCurl; public float ringCurl; public float littleCurl; public bool syncSwing; public float thumbSwing; public float indexSwing; public float middleSwing; public float ringSwing; public float littleSwing; public HumanoidHandPose() : base(Tracking.Bone.None) { } public HumanoidHandPose(Tracking.Bone boneId) : base(boneId) { } public HumanoidHandPose(HandTarget handTarget, bool syncSwing) : base(handTarget) { this.syncSwing = syncSwing; FingersTarget.UpdateCurlValues(handTarget); //FingersTarget.UpdateCurlValues(handTarget); thumbCurl = handTarget.fingers.thumb.curl; indexCurl = handTarget.fingers.index.curl; middleCurl = handTarget.fingers.middle.curl; ringCurl = handTarget.fingers.ring.curl; littleCurl = handTarget.fingers.little.curl; thumbSwing = handTarget.fingers.thumb.swing; indexSwing = handTarget.fingers.index.swing; middleSwing = handTarget.fingers.middle.swing; ringSwing = handTarget.fingers.ring.swing; littleSwing = handTarget.fingers.little.swing; } public override void Write(BinaryWriter bw) { base.Write(bw); if (boneId != Tracking.Bone.None) { bw.Write(thumbCurl); bw.Write(indexCurl); bw.Write(middleCurl); bw.Write(ringCurl); bw.Write(littleCurl); } } public static new HumanoidHandPose Read(BinaryReader br) { Tracking.Bone boneId = (Tracking.Bone)br.ReadSByte(); if (boneId == Tracking.Bone.None) return new HumanoidHandPose(); else return new HumanoidHandPose(boneId) { localPosition = Vector3S.Read(br), positionConfidenceByte = br.ReadByte(), rotation = Vector3S.Read(br), rotationConfidenceByte = br.ReadByte(), thumbCurl = br.ReadSingle(), indexCurl = br.ReadSingle(), middleCurl = br.ReadSingle(), ringCurl = br.ReadSingle(), littleCurl = br.ReadSingle(), }; } } [Serializable] public class HumanoidTargetPose { public Tracking.Bone boneId; public Vector3S localPosition; protected byte positionConfidenceByte; public float positionConfidence { get { return ToFloat(positionConfidenceByte); } } public Vector3S rotation; protected byte rotationConfidenceByte; public float rotationConfidence { get { return ToFloat(rotationConfidenceByte); } } public float ToFloat(byte confidenceByte) { float value = Convert.ToSingle(confidenceByte) / 255; return value; } public HumanoidTargetPose() { this.boneId = Tracking.Bone.None; localPosition = new Vector3S(); rotation = new Vector3S(); } public HumanoidTargetPose(Tracking.Bone boneId) { this.boneId = boneId; localPosition = new Vector3S(); rotation = new Vector3S(); } public HumanoidTargetPose(HumanoidTarget target) { boneId = GetBoneId(target); // Head bone is always synced if (boneId != Tracking.Bone.Head && !HumanoidPose.TargetActive(target)) boneId = Tracking.Bone.None; Vector3 localPosition = target.humanoid.transform.InverseTransformPoint(target.main.target.transform.position); this.localPosition = new Vector3S(localPosition); positionConfidenceByte = Convert.ToByte(target.main.target.confidence.position * 255); rotation = new Vector3S(target.main.target.transform.rotation); rotationConfidenceByte = Convert.ToByte(target.main.target.confidence.rotation * 255); } private Tracking.Bone GetBoneId(HumanoidTarget target) { if (target == target.humanoid.hipsTarget) return Tracking.Bone.Hips; else if (target == target.humanoid.headTarget) return Tracking.Bone.Head; else if (target == target.humanoid.leftHandTarget) return Tracking.Bone.LeftHand; else if (target == target.humanoid.rightHandTarget) return Tracking.Bone.RightHand; else if (target == target.humanoid.leftFootTarget) return Tracking.Bone.LeftFoot; else if (target == target.humanoid.rightFootTarget) return Tracking.Bone.RightFoot; else return Tracking.Bone.None; } public virtual void Write(BinaryWriter bw) { bw.Write((sbyte)boneId); if (boneId != Tracking.Bone.None) { localPosition.Write(bw); bw.Write(positionConfidenceByte); rotation.Write(bw); bw.Write(rotationConfidenceByte); } } public static HumanoidTargetPose Read(BinaryReader br) { Tracking.Bone boneId = (Tracking.Bone)br.ReadSByte(); if (boneId == Tracking.Bone.None) return new HumanoidTargetPose(); else { return new HumanoidTargetPose(boneId) { localPosition = Vector3S.Read(br), positionConfidenceByte = br.ReadByte(), rotation = Vector3S.Read(br), rotationConfidenceByte = br.ReadByte() }; } } } [Serializable] public class HumanoidPose : IMessage { public ulong nwId; public byte humanoidId; public float poseTime; public float receiveTime; public Vector3S position; public Vector3S rotation; public HumanoidAnimatorParameters animatorParameters; //public byte targetMask; public HumanoidTargetPose hips; public HumanoidTargetPose head; public HumanoidHandPose leftHand; public HumanoidHandPose rightHand; public HumanoidTargetPose leftFoot; public HumanoidTargetPose rightFoot; public bool syncFace; #if hFACE public HumanoidFacePose faceTarget; #endif public HumanoidPose() { animatorParameters = new HumanoidAnimatorParameters(); hips = new HumanoidTargetPose(Tracking.Bone.None); head = new HumanoidTargetPose(Tracking.Bone.None); leftHand = new HumanoidHandPose(Tracking.Bone.None); rightHand = new HumanoidHandPose(Tracking.Bone.None); leftFoot = new HumanoidTargetPose(Tracking.Bone.None); rightFoot = new HumanoidTargetPose(Tracking.Bone.None); #if hFACE faceTarget = new HumanoidFacePose(); #endif } public HumanoidPose(HumanoidControl humanoid, float poseTime, bool syncFingerSwing = false, bool syncFace = false) { this.syncFace = syncFace; nwId = humanoid.nwId; humanoidId = (byte)humanoid.humanoidId; this.poseTime = poseTime; position = new Vector3S(humanoid.transform.position); rotation = new Vector3S(humanoid.transform.rotation); animatorParameters = new HumanoidAnimatorParameters(humanoid); hips = new HumanoidTargetPose(humanoid.hipsTarget); head = new HumanoidTargetPose(humanoid.headTarget); leftHand = new HumanoidHandPose(humanoid.leftHandTarget, syncFingerSwing); rightHand = new HumanoidHandPose(humanoid.rightHandTarget, syncFingerSwing); leftFoot = new HumanoidTargetPose(humanoid.leftFootTarget); rightFoot = new HumanoidTargetPose(humanoid.rightFootTarget); #if hFACE faceTarget = new HumanoidFacePose(humanoid.headTarget.face); #endif } public HumanoidPose(byte[] data) : base(data) { } public static bool TargetActive(HumanoidTarget target) { //bool active = target.main != null && // (target.main.target.confidence.position > 0.2F || target.main.target.confidence.rotation > 0.2F); // if (!target.animator.enabled) return true; // return active; } public override byte[] Serialize() { MemoryStream ms = new MemoryStream(); BinaryWriter bw = new BinaryWriter(ms); bw.Write(nwId); bw.Write(humanoidId); bw.Write(poseTime); position.Write(bw); rotation.Write(bw); animatorParameters.Write(bw); hips.Write(bw); head.Write(bw); leftHand.Write(bw); rightHand.Write(bw); leftFoot.Write(bw); rightFoot.Write(bw); #if hFACE if (syncFace) faceTarget.Write(bw); #endif byte[] data = ms.ToArray(); return data; } public override void Deserialize(byte[] data) { MemoryStream ms = new MemoryStream(data); BinaryReader br = new BinaryReader(ms); nwId = br.ReadUInt64(); humanoidId = br.ReadByte(); poseTime = br.ReadSingle(); position = Vector3S.Read(br); rotation = Vector3S.Read(br); animatorParameters = HumanoidAnimatorParameters.Read(br); hips = HumanoidTargetPose.Read(br); head = HumanoidTargetPose.Read(br); leftHand = HumanoidHandPose.Read(br); rightHand = HumanoidHandPose.Read(br); leftFoot = HumanoidTargetPose.Read(br); rightFoot = HumanoidTargetPose.Read(br); #if hFACE if (syncFace) faceTarget = HumanoidFacePose.Read(br); #endif } // This is the same code as in the base class IMessage // It is needed because the base class implementation only // results in compiler errors. #if hNW_MIRROR //public override void Serialize(Mirror.NetworkWriter writer) { // byte[] data = Serialize(); // Mirror.NetworkWriterExtensions.WriteBytesAndSize(writer, data); //} //public override void Deserialize(Mirror.NetworkReader reader) { // byte[] data = Mirror.NetworkReaderExtensions.ReadBytesAndSize(reader); // Deserialize(data); //} #endif public HumanoidTargetPose GetTargetPose(Tracking.Bone boneId) { switch (boneId) { case Tracking.Bone.Hips: return hips; case Tracking.Bone.Head: return head; case Tracking.Bone.LeftHand: return leftHand; case Tracking.Bone.RightHand: return rightHand; case Tracking.Bone.LeftFoot: return leftFoot; case Tracking.Bone.RightFoot: return rightFoot; default: return null; } } } #if hFACE [Serializable] public class HumanoidFacePose { public short targetMask; public sbyte leftBrowOuterRaise; public sbyte leftBrowInnerRaise; public sbyte rightBrowOuterRaise; public sbyte rightBrowInnerRaise; public sbyte leftEyeClosed; public sbyte rightEyeClosed; public sbyte mouthLeftRaise; public sbyte mouthRightRaise; public sbyte mouthLeftStretch; public sbyte mouthRightStretch; public sbyte mouthShiftRight; public sbyte jawOpen; public sbyte jawShiftRight; public HumanoidFacePose() { } public HumanoidFacePose(FaceTarget face) { leftBrowOuterRaise = (sbyte)(face.leftBrow.outerRaise * 127); leftBrowInnerRaise = (sbyte)(face.leftBrow.innerRaise * 127); rightBrowOuterRaise = (sbyte)(face.rightBrow.outerRaise * 127); rightBrowInnerRaise = (sbyte)(face.rightBrow.innerRaise * 127); leftEyeClosed = (sbyte)(face.leftEye.closed * 127); rightEyeClosed = (sbyte)(face.rightEye.closed * 127); mouthLeftRaise = (sbyte)(face.mouth.leftRaise * 127); mouthRightRaise = (sbyte)(face.mouth.rightRaise * 127); mouthLeftStretch = (sbyte)(face.mouth.leftStretch * 127); mouthRightStretch = (sbyte)(face.mouth.rightStretch * 127); mouthShiftRight = (sbyte)(face.mouth.shiftRight * 127); jawOpen = (sbyte)(face.jaw.open * 127); jawShiftRight = (sbyte)(face.jaw.shiftRight * 127); } public void Write(BinaryWriter bw) { bw.Write(leftBrowOuterRaise); bw.Write(leftBrowInnerRaise); bw.Write(rightBrowOuterRaise); bw.Write(rightBrowInnerRaise); bw.Write(leftEyeClosed); bw.Write(rightEyeClosed); bw.Write(mouthLeftRaise); bw.Write(mouthRightRaise); bw.Write(mouthLeftStretch); bw.Write(mouthRightStretch); bw.Write(mouthShiftRight); bw.Write(jawOpen); bw.Write(jawShiftRight); } public static HumanoidFacePose Read(BinaryReader br) { HumanoidFacePose pose = new HumanoidFacePose(); pose.leftBrowOuterRaise = br.ReadSByte(); pose.leftBrowInnerRaise = br.ReadSByte(); pose.rightBrowOuterRaise = br.ReadSByte(); pose.rightBrowInnerRaise = br.ReadSByte(); pose.leftEyeClosed = br.ReadSByte(); pose.rightEyeClosed = br.ReadSByte(); pose.mouthLeftRaise = br.ReadSByte(); pose.mouthRightRaise = br.ReadSByte(); pose.mouthLeftStretch = br.ReadSByte(); pose.mouthRightStretch = br.ReadSByte(); pose.mouthShiftRight = br.ReadSByte(); pose.jawOpen = br.ReadSByte(); pose.jawShiftRight = br.ReadSByte(); return pose; } } #endif public static void ReceiveHumanoidPose(this IHumanoidNetworking networking, byte[] data) { if (data == null) return; HumanoidPose humanoidPose = new HumanoidPose(data); Receive(networking, humanoidPose); } public static void Receive(this IHumanoidNetworking receivingNetworking, HumanoidPose humanoidPose) { //DebugHumanoidPose(humanoidPose); humanoidPose.receiveTime = Time.time; IHumanoidNetworking networking = GetHumanoidNetworking(receivingNetworking, humanoidPose.nwId); if (networking == null) { if (receivingNetworking.debug <= DebugLevel.Error) receivingNetworking.DebugLog("Could not find Networking for Humanoid Pose " + humanoidPose.nwId + "/" + humanoidPose.humanoidId); return; } if (networking.isLocal && !networking.createLocalRemotes) return; if (networking.humanoids == null) { networking.DebugLog("No humanoids found"); return; } HumanoidControl remoteHumanoid = FindRemoteHumanoid(networking.humanoids, humanoidPose.humanoidId); if (remoteHumanoid == null) { if (networking.debug <= DebugLevel.Warning) networking.DebugWarning("Could not find humanoid: " + humanoidPose.nwId + "/" + humanoidPose.humanoidId); return; } if (networking.debug <= DebugLevel.Debug) networking.DebugLog("Receive HumanoidPose " + humanoidPose.nwId + "/" + humanoidPose.humanoidId); ReceiveHumanoidPose(remoteHumanoid, humanoidPose, networking.lastHumanoidPose, networking.smoothing); if (networking.createLocalRemotes) remoteHumanoid.transform.Translate(0, 0, 1, Space.Self); networking.lastHumanoidPose = humanoidPose; } private static void DebugHumanoidPose(HumanoidPose pose) { string s = ""; if (pose.hips.boneId == Tracking.Bone.Hips) s += " HI_" + pose.hips.positionConfidence + "/" + pose.hips.rotationConfidence; if (pose.head.boneId == Tracking.Bone.Head) s += " HE_" + pose.head.positionConfidence + "/" + pose.head.rotationConfidence; if (pose.leftHand.boneId == Tracking.Bone.LeftHand) s += " LH_" + pose.leftHand.positionConfidence + "/" + pose.leftHand.rotationConfidence; if (pose.rightHand.boneId == Tracking.Bone.RightHand) s += " RH_" + pose.rightHand.positionConfidence + "/" + pose.rightHand.rotationConfidence; if (pose.leftFoot.boneId == Tracking.Bone.LeftFoot) s += " LH_" + pose.leftFoot.positionConfidence + "/" + pose.leftFoot.rotationConfidence; if (pose.rightFoot.boneId == Tracking.Bone.RightFoot) s += " RH_" + pose.rightFoot.positionConfidence + "/" + pose.rightFoot.rotationConfidence; Debug.Log(s); } public static void ReceiveHumanoidPose( HumanoidControl remoteHumanoid, HumanoidPose humanoidPose, HumanoidPose lastHumanoidPose, HumanoidNetworking.Smoothing smoothing) { remoteHumanoid.transform.position = humanoidPose.position.vector3; remoteHumanoid.transform.rotation = humanoidPose.rotation.quaternion; if (lastHumanoidPose != null) CalculateVelocity(remoteHumanoid, humanoidPose.position.vector3, lastHumanoidPose.position.vector3, humanoidPose.poseTime, humanoidPose.receiveTime, lastHumanoidPose.poseTime); ReceiveAnimationParameters(remoteHumanoid, humanoidPose); remoteHumanoid.headTarget.animator.enabled = true; remoteHumanoid.leftHandTarget.animator.enabled = true; remoteHumanoid.rightHandTarget.animator.enabled = true; remoteHumanoid.hipsTarget.animator.enabled = true; remoteHumanoid.leftFootTarget.animator.enabled = true; remoteHumanoid.rightFootTarget.animator.enabled = true; ReceiveTargetPose(remoteHumanoid, humanoidPose, humanoidPose.hips, lastHumanoidPose, smoothing); ReceiveTargetPose(remoteHumanoid, humanoidPose, humanoidPose.head, lastHumanoidPose, smoothing); HeadAnimator.UpdateNeckTargetFromHead(remoteHumanoid.headTarget); ReceiveTargetPose(remoteHumanoid, humanoidPose, humanoidPose.leftHand, lastHumanoidPose, smoothing); ReceiveTargetPose(remoteHumanoid, humanoidPose, humanoidPose.rightHand, lastHumanoidPose, smoothing); ReceiveTargetPose(remoteHumanoid, humanoidPose, humanoidPose.leftFoot, lastHumanoidPose, smoothing); ReceiveTargetPose(remoteHumanoid, humanoidPose, humanoidPose.rightFoot, lastHumanoidPose, smoothing); remoteHumanoid.CopyRigToTargets(); } private static bool ReceiveAnimationParameters(HumanoidControl humanoid, HumanoidPose pose) { if (humanoid.targetsRig.runtimeAnimatorController == null) { if (pose.animatorParameters.parameters.Length > 0) Debug.LogWarning("Animation Controller is missing on remote Humanoid.\nAnitaion will not be synchronized"); return false; } for (int i = 0; i < humanoid.targetsRig.parameterCount; i++) { AnimatorControllerParameter parameter = humanoid.targetsRig.parameters[i]; switch (parameter.type) { case AnimatorControllerParameterType.Bool: humanoid.targetsRig.SetBool(parameter.name, pose.animatorParameters.parameters[i].boolValue); break; case AnimatorControllerParameterType.Float: humanoid.targetsRig.SetFloat(parameter.name, pose.animatorParameters.parameters[i].floatValue); break; case AnimatorControllerParameterType.Int: humanoid.targetsRig.SetInteger(parameter.name, pose.animatorParameters.parameters[i].intValue); break; case AnimatorControllerParameterType.Trigger: humanoid.targetsRig.SetBool(parameter.name, pose.animatorParameters.parameters[i].boolValue); break; } } return true; } private static void ReceiveTargetPose( HumanoidControl humanoid, HumanoidPose humanoidPose, HumanoidTargetPose targetPose, HumanoidPose lastPose, Smoothing smoothing) { if (targetPose == null) { return; } HumanoidTarget target = GetTarget(humanoid, targetPose.boneId); if (target != null) target.animator.enabled = false; //target.EnableAnimator(false); if (targetPose.boneId == Tracking.Bone.LeftHand || targetPose.boneId == Tracking.Bone.RightHand) ReceiveHand((HandTarget)target, (HumanoidHandPose)targetPose); if (lastPose != null) { HumanoidTargetPose lastTargetPose = lastPose.GetTargetPose(targetPose.boneId); if (lastTargetPose != null) { ReceiveTarget(target, targetPose, lastTargetPose, humanoidPose.poseTime, humanoidPose.receiveTime, lastPose.poseTime, lastPose.receiveTime, smoothing); return; } } ReceiveTarget(target, targetPose); } private static HumanoidTarget GetTarget(HumanoidControl humanoid, Tracking.Bone boneId) { switch (boneId) { case Tracking.Bone.Hips: return humanoid.hipsTarget; case Tracking.Bone.Head: return humanoid.headTarget; case Tracking.Bone.LeftHand: return humanoid.leftHandTarget; case Tracking.Bone.RightHand: return humanoid.rightHandTarget; case Tracking.Bone.LeftFoot: return humanoid.leftFootTarget; case Tracking.Bone.RightFoot: return humanoid.rightFootTarget; default: return null; } } #region Receive Target private static void ReceiveTarget(HumanoidTarget target, HumanoidTargetPose targetPose) { if (target == null || targetPose == null) return; HumanoidTarget.TargetTransform targetTransform = target.main.target; if (targetPose.positionConfidence >= targetTransform.confidence.position) { targetTransform.transform.position = target.humanoid.transform.TransformPoint(targetPose.localPosition.vector3); targetTransform.confidence.position = targetPose.positionConfidence; } if (targetPose.rotationConfidence >= targetTransform.confidence.rotation) { targetTransform.transform.rotation = targetPose.rotation.quaternion; targetTransform.confidence.rotation = targetPose.rotationConfidence; } } private static void ReceiveTarget( HumanoidTarget target, HumanoidTargetPose targetPose, HumanoidTargetPose lastTargetPose, float poseTime, float receiveTime, float lastPoseTime, float lastReceiveTime, Smoothing smoothing) { if (target == null || targetPose == null) return; HumanoidTarget.TargetTransform targetTransform = target.main.target; if (targetPose.positionConfidence >= targetTransform.confidence.position) { // This code results in wrong hand positions: //Vector3 oldLocalPosition = target.humanoid.transform.InverseTransformPoint(target.transform.position); //targetTransform.transform.position = // CorrectedLocalPosition(oldLocalPosition, target.humanoid.transform, targetPose.localPosition.vector3, lastTargetPose.localPosition.vector3, poseTime, receiveTime, lastPoseTime, lastReceiveTime, smoothing); targetTransform.transform.position = target.humanoid.transform.TransformPoint(targetPose.localPosition.vector3); targetTransform.confidence.position = targetPose.positionConfidence; } if (targetPose.rotationConfidence >= targetTransform.confidence.rotation) { targetTransform.transform.rotation = targetPose.rotation.quaternion; targetTransform.confidence.rotation = targetPose.rotationConfidence; } } ///Update the transform position with correction for transport jitter private static Vector3 CorrectedPosition( Transform targetTransform, Vector3 receivedPosition, Vector3 lastReceivedPosition, float poseTime, float receiveTime, float lastPoseTime, float lastReceiveTime, Smoothing smoothing) { Vector3 newTargetPosition = receivedPosition; float deltaPoseTime = poseTime - lastPoseTime; float deltaReceiveTime = receiveTime - lastReceiveTime; if (deltaPoseTime > 0 && deltaReceiveTime > 0 && lastReceivedPosition != Vector3.zero) { Vector3 receivedTranslation = receivedPosition - lastReceivedPosition; Vector3 translation = receivedTranslation * (deltaReceiveTime / deltaPoseTime); if (smoothing == Smoothing.None) newTargetPosition = targetTransform.position + translation; else if (smoothing == Smoothing.Extrapolation) newTargetPosition = receivedPosition; else if (smoothing == Smoothing.Interpolation) newTargetPosition = lastReceivedPosition; } lastReceiveTime = receiveTime; lastPoseTime = poseTime; return newTargetPosition; } ///Update the transform position with correction for transport jitter private static Vector3 CorrectedLocalPosition( Vector3 oldLocalPosition, Transform parentTransform, Vector3 receivedPosition, Vector3 lastReceivedPosition, float poseTime, float receiveTime, float lastPoseTime, float lastReceiveTime, Smoothing smoothing) { Vector3 newLocalPosition = receivedPosition; float deltaPoseTime = poseTime - lastPoseTime; float deltaReceiveTime = receiveTime - lastReceiveTime; if (deltaPoseTime > 0 && deltaReceiveTime > 0 && lastReceivedPosition != Vector3.zero) { Vector3 receivedTranslation = receivedPosition - lastReceivedPosition; Vector3 translation = receivedTranslation * (deltaReceiveTime / deltaPoseTime); if (smoothing != Smoothing.Interpolation) newLocalPosition = oldLocalPosition + translation; } lastReceiveTime = receiveTime; lastPoseTime = poseTime; return parentTransform.TransformPoint(newLocalPosition); } private static void CalculateVelocity( HumanoidControl humanoid, Vector3 receivedPosition, Vector3 lastReceivedPosition, float poseTime, float receiveTime, float lastPoseTime) { float deltaTime = poseTime - lastPoseTime; if (deltaTime > 0) { Vector3 translation = receivedPosition - lastReceivedPosition; humanoid.velocity = translation / deltaTime; } } #endregion private static void ReceiveHand(HandTarget handTarget, HumanoidHandPose handPose) { if (handPose == null) return; FingersTarget fingersTarget = handTarget.fingers; fingersTarget.thumb.curl = handPose.thumbCurl; fingersTarget.index.curl = handPose.indexCurl; fingersTarget.middle.curl = handPose.middleCurl; fingersTarget.ring.curl = handPose.ringCurl; fingersTarget.little.curl = handPose.littleCurl; if (handPose.syncSwing) { fingersTarget.thumb.swing = handPose.thumbSwing; fingersTarget.index.swing = handPose.indexSwing; fingersTarget.middle.swing = handPose.middleSwing; fingersTarget.ring.swing = handPose.ringSwing; fingersTarget.little.swing = handPose.littleSwing; } } #if hFACE private static void ReceiveFace(FaceTarget faceTarget, HumanoidFacePose facePose) { if (facePose == null) return; faceTarget.leftBrow.outerRaise = ((float)facePose.leftBrowOuterRaise) / 127; faceTarget.leftBrow.innerRaise = ((float)facePose.leftBrowInnerRaise) / 127; faceTarget.rightBrow.outerRaise = ((float)facePose.rightBrowOuterRaise) / 127; faceTarget.rightBrow.innerRaise = ((float)facePose.rightBrowInnerRaise) / 127; faceTarget.leftEye.closed = ((float)facePose.leftEyeClosed) / 127; faceTarget.rightEye.closed = ((float)facePose.rightEyeClosed) / 127; faceTarget.mouth.leftRaise = ((float)facePose.mouthLeftRaise) / 127; faceTarget.mouth.rightRaise = ((float)facePose.mouthRightRaise) / 127; faceTarget.mouth.leftStretch = ((float)facePose.mouthLeftStretch) / 127; faceTarget.mouth.rightStretch = ((float)facePose.mouthRightStretch) / 127; faceTarget.mouth.shiftRight = ((float)facePose.mouthShiftRight) / 127; faceTarget.jaw.open = ((float)facePose.jawOpen) / 127; faceTarget.jaw.shiftRight = ((float)facePose.jawShiftRight) / 127; faceTarget.UpdateMorphTargets(); } #endif public static bool IsTargetActive(byte targetMask, HumanoidControl.TargetId targetIndex) { int bitset = targetMask & (byte)(1 << ((int)targetIndex + 1)); return (bitset != 0); } #endregion #region Grab public class Grab : IMessage { public ulong nwId; public byte humanoidId; public bool isLeft; public ulong nwId_grabbedObject; public bool rangeCheck; public HandTarget.GrabType grabType; public Grab() { } public Grab(HandTarget handTarget, ulong nwId_grabbedObject, bool rangeCheck, HandTarget.GrabType grabType) { nwId = handTarget.humanoid.nwId; humanoidId = (byte)handTarget.humanoid.humanoidId; isLeft = handTarget.isLeft; this.nwId_grabbedObject = nwId_grabbedObject; this.rangeCheck = rangeCheck; this.grabType = grabType; } public Grab(byte[] data) : base(data) { } public override byte[] Serialize() { MemoryStream ms = new MemoryStream(); BinaryWriter bw = new BinaryWriter(ms); bw.Write(nwId); bw.Write(humanoidId); bw.Write(isLeft); bw.Write(nwId_grabbedObject); bw.Write(rangeCheck); bw.Write((int)grabType); byte[] data = ms.ToArray(); return data; } public override void Deserialize(byte[] data) { MemoryStream ms = new MemoryStream(data); BinaryReader br = new BinaryReader(ms); nwId = br.ReadUInt64(); humanoidId = br.ReadByte(); isLeft = br.ReadBoolean(); nwId_grabbedObject = br.ReadUInt64(); rangeCheck = br.ReadBoolean(); grabType = (HandTarget.GrabType)br.ReadInt32(); } // This is the same code as in the base class IMessage // It is needed because the base class implementation only // results in compiler errors. #if hNW_MIRROR //public override void Serialize(Mirror.NetworkWriter writer) { // byte[] data = Serialize(); // Mirror.NetworkWriterExtensions.WriteBytesAndSize(writer, data); //} //public override void Deserialize(Mirror.NetworkReader reader) { // byte[] data = Mirror.NetworkReaderExtensions.ReadBytesAndSize(reader); // Deserialize(data); //} #endif } public static void ReceiveGrab(this IHumanoidNetworking networking, byte[] serializedData) { Grab data = new Grab(serializedData); Receive(networking, data); } public static void Receive(this IHumanoidNetworking networking, Grab msg) { GameObject obj = networking.GetGameObject(msg.nwId_grabbedObject); if (networking.debug <= DebugLevel.Info) networking.DebugLog("GrabEvent " + obj); HumanoidControl humanoid = HumanoidNetworking.FindRemoteHumanoid(networking.humanoids, msg.humanoidId); if (humanoid == null) { if (networking.debug <= DebugLevel.Warning) networking.DebugWarning("Could not find humanoid: " + msg.humanoidId); return; } HandTarget handTarget = msg.isLeft ? humanoid.leftHandTarget : humanoid.rightHandTarget; handTarget.Grab(obj, msg.rangeCheck); } #endregion #region LetGo public class LetGo : IMessage { public ulong nwId; public byte humanoidId; public bool isLeft; public LetGo() { } public LetGo(HandTarget handTarget) { nwId = handTarget.humanoid.nwId; humanoidId = (byte)handTarget.humanoid.humanoidId; isLeft = handTarget.isLeft; } public LetGo(byte[] data) : base(data) { } public override byte[] Serialize() { MemoryStream ms = new MemoryStream(); BinaryWriter bw = new BinaryWriter(ms); bw.Write(nwId); bw.Write(humanoidId); bw.Write(isLeft); byte[] data = ms.ToArray(); return data; } public override void Deserialize(byte[] data) { MemoryStream ms = new MemoryStream(data); BinaryReader br = new BinaryReader(ms); nwId = br.ReadUInt64(); humanoidId = br.ReadByte(); isLeft = br.ReadBoolean(); } // This is the same code as in the base class IMessage // It is needed because the base class implementation only // results in compiler errors. #if hNW_MIRROR //public override void Serialize(Mirror.NetworkWriter writer) { // byte[] data = Serialize(); // Mirror.NetworkWriterExtensions.WriteBytesAndSize(writer, data); //} //public override void Deserialize(Mirror.NetworkReader reader) { // byte[] data = Mirror.NetworkReaderExtensions.ReadBytesAndSize(reader); // Deserialize(data); //} #endif } public static void ReceiveLetGo(this IHumanoidNetworking networking, byte[] serializedData) { LetGo data = new LetGo(serializedData); Receive(networking, data); } public static void Receive(this IHumanoidNetworking networking, LetGo msg) { HumanoidControl humanoid = FindRemoteHumanoid(networking.humanoids, msg.humanoidId); if (humanoid == null) { if (networking.debug <= DebugLevel.Warning) Debug.LogWarning("Could not find humanoid: " + msg.humanoidId); return; } HandTarget handTarget = msg.isLeft ? humanoid.leftHandTarget : humanoid.rightHandTarget; handTarget.LetGo(); } #endregion #region Change Avatar public class ChangeAvatar : IMessage { public ulong nwId; public byte humanoidId; public string avatarPrefabName; public string possessionLocation = ""; public ChangeAvatar() { } public ChangeAvatar(HumanoidControl humanoid, string avatarPrefabName, string possessionLocation) { nwId = humanoid.nwId; humanoidId = (byte)humanoid.humanoidId; this.avatarPrefabName = avatarPrefabName; this.possessionLocation = possessionLocation; } public ChangeAvatar(byte[] data) : base(data) { } public override byte[] Serialize() { MemoryStream ms = new MemoryStream(); BinaryWriter bw = new BinaryWriter(ms); bw.Write(nwId); bw.Write(humanoidId); bw.Write(avatarPrefabName); if (possessionLocation == null) possessionLocation = ""; bw.Write(possessionLocation); byte[] data = ms.ToArray(); return data; } public override void Deserialize(byte[] data) { MemoryStream ms = new MemoryStream(data); BinaryReader br = new BinaryReader(ms); nwId = br.ReadUInt64(); humanoidId = br.ReadByte(); avatarPrefabName = br.ReadString(); possessionLocation = br.ReadString(); if (possessionLocation == "") possessionLocation = null; } // This is the same code as in the base class IMessage // It is needed because the base class implementation only // results in compiler errors. #if hNW_MIRROR //public override void Serialize(Mirror.NetworkWriter writer) { // byte[] data = Serialize(); // Mirror.NetworkWriterExtensions.WriteBytesAndSize(writer, data); //} //public override void Deserialize(Mirror.NetworkReader reader) { // byte[] data = Mirror.NetworkReaderExtensions.ReadBytesAndSize(reader); // Deserialize(data); //} #endif } public static void ReceiveChangeAvatar(this IHumanoidNetworking networking, byte[] serializedData) { ChangeAvatar data = new ChangeAvatar(serializedData); Receive(networking, data); } public static void Receive(this IHumanoidNetworking receivingNetworking, ChangeAvatar msg) { IHumanoidNetworking networking = GetHumanoidNetworking(receivingNetworking, msg.nwId); if (networking == null) { if (receivingNetworking.debug <= DebugLevel.Error) receivingNetworking.DebugLog("Could not find Networking for Humanoid Pose " + msg.nwId + "/" + msg.humanoidId); return; } if (networking.isLocal && !networking.createLocalRemotes) { networking.DebugLog("Don't change the local avatar"); return; } HumanoidControl remoteHumanoid = FindRemoteHumanoid(networking.humanoids, msg.humanoidId); if (remoteHumanoid == null) { if (networking.debug <= DebugLevel.Warning) Debug.LogWarning("Could not find humanoid: " + msg.humanoidId); return; } if (msg.possessionLocation != null) { Debug.Log("Need to download avatar possession from " + msg.possessionLocation); HumanoidPlayer.instance.StartCoroutine( VisitorPossessions.RetrievePossessableAsync(msg.possessionLocation, msg.avatarPrefabName, retrievedAvatar => ProcessRetrievedAvatar(networking, remoteHumanoid, retrievedAvatar)) ); return; } GameObject remoteAvatar = (GameObject)Resources.Load(msg.avatarPrefabName); if (remoteAvatar == null) { if (networking.debug <= DebugLevel.Error) Debug.LogError("Could not load remote avatar " + msg.avatarPrefabName + ". Is it located in a Resources folder?"); return; } if (networking.debug <= DebugLevel.Info) networking.DebugLog("Receive Change Avatar " + msg.nwId + "/" + msg.humanoidId + " " + msg.avatarPrefabName); remoteHumanoid.LocalChangeAvatar(remoteAvatar); } private static void ProcessRetrievedAvatar(IHumanoidNetworking networking, HumanoidControl remoteHumanoid, GameObject retrievedAvatar) { if (retrievedAvatar == null) { if (networking.debug <= DebugLevel.Error) Debug.LogError("Could not retrieve avatar."); return; } if (networking.debug <= DebugLevel.Info) networking.DebugLog("Receive Change Possessesable Avatar " + retrievedAvatar); remoteHumanoid.LocalChangeAvatar(retrievedAvatar); } #endregion #region Sync Tracking Space public class SyncTrackingSpace : IMessage { public ulong nwId; public byte humanoidId; public Vector3S position; public Vector3S rotation; public SyncTrackingSpace() { } public SyncTrackingSpace(HumanoidControl humanoid, Vector3 position, Quaternion rotation) { nwId = humanoid.nwId; humanoidId = (byte)humanoid.humanoidId; this.position = new Vector3S(position); this.rotation = new Vector3S(rotation); } public SyncTrackingSpace(byte[] data) : base(data) { } public override byte[] Serialize() { MemoryStream ms = new MemoryStream(); BinaryWriter bw = new BinaryWriter(ms); bw.Write(nwId); bw.Write(humanoidId); position.Write(bw); rotation.Write(bw); byte[] data = ms.ToArray(); return data; } public override void Deserialize(byte[] data) { MemoryStream ms = new MemoryStream(data); BinaryReader br = new BinaryReader(ms); nwId = br.ReadUInt64(); humanoidId = br.ReadByte(); position = Vector3S.Read(br); rotation = Vector3S.Read(br); } // This is the same code as in the base class IMessage // It is needed because the base class implementation only // results in compiler errors. #if hNW_MIRROR //public override void Serialize(Mirror.NetworkWriter writer) { // byte[] data = Serialize(); // Mirror.NetworkWriterExtensions.WriteBytesAndSize(writer, data); //} //public override void Deserialize(Mirror.NetworkReader reader) { // byte[] data = Mirror.NetworkReaderExtensions.ReadBytesAndSize(reader); // Deserialize(data); //} #endif } public static Transform GetTrackingTransform(HumanoidControl humanoid) { #if hANTILATENCY if (humanoid.antilatency != null) return humanoid.antilatency.trackerTransform; #endif //#if hOPENVR && (UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX) // if (humanoid.openVR != null) // return humanoid.openVR.GetTrackingTransform(); //#endif return null; } public static void ReceiveSyncTrackingSpace(this IHumanoidNetworking networking, byte[] serializedData) { SyncTrackingSpace data = new SyncTrackingSpace(serializedData); Receive(networking, data); } public static void Receive(this IHumanoidNetworking networking, SyncTrackingSpace msg) { foreach (HumanoidControl humanoid in HumanoidControl.allHumanoids) { if (humanoid.isRemote || humanoid.nwId == msg.nwId) continue; // The lowest (= earliest) nwId is the boss if (msg.nwId > humanoid.nwId) return; Debug.Log("receive tracking sync"); //#if hOPENVR && (UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX) // // The lowest (= earliest) nwId is the boss // // NOT ATM FOR TESTING // //if (msg.nwId > humanoid.nwId) // if (humanoid.openVR != null) { // humanoid.openVR.SyncTracking(msg.position.vector3, msg.rotation.quaternion); // } //#endif #if hANTILATENCY //if (msg.nwId > humanoid.nwId) { if (humanoid.antilatency != null) { humanoid.antilatency.SyncTracking(msg.position.vector3, msg.rotation.quaternion); } //} #endif } } #endregion public static IHumanoidNetworking GetHumanoidNetworking(IHumanoidNetworking networking, ulong nwId) { if (networking.nwId == nwId) return networking; GameObject networkingObj = networking.GetGameObject(nwId); if (networkingObj == null) return null; return networkingObj.GetComponent(); } public static List FindLocalHumanoids() { List humanoidList = new List(); HumanoidControl[] foundHumanoids = UnityEngine.Object.FindObjectsOfType(); for (int i = 0; i < foundHumanoids.Length; i++) { if (!foundHumanoids[i].isRemote) { humanoidList.Add(foundHumanoids[i]); } } return humanoidList; } public static IHumanoidNetworking GetLocalHumanoidNetworking() { HumanoidPlayer[] humanoidNetworkings = UnityEngine.Object.FindObjectsOfType(); foreach (IHumanoidNetworking humanoidNetworking in humanoidNetworkings) { if (humanoidNetworking.isLocal) return humanoidNetworking; } return null; } public static void DisableNetworkSync(GameObject obj) { #if hNW_UNET || hNW_PHOTON || hNW_BOLT || hNW_MIRROR HumanoidPlayer.DisableNetworkSync(obj); #endif } public static void ReenableNetworkSync(GameObject obj) { #if hNW_UNET || hNW_PHOTON || hNW_BOLT || hNW_MIRROR HumanoidPlayer.ReenableNetworkSync(obj); #endif } public static void TakeOwnership(GameObject obj) { #if hNW_PHOTON HumanoidPlayer.TakeOwnership(obj); #endif } public static HumanoidControl FindHumanoid(List humanoids, int humanoidId) { foreach (HumanoidControl humanoid in humanoids) { if (humanoid == null) continue; if (humanoid.humanoidId == humanoidId) return humanoid; } return null; } public static HumanoidControl FindLocalHumanoid(List humanoids, int humanoidId) { foreach (HumanoidControl humanoid in humanoids) { if (humanoid == null) continue; if (!humanoid.isRemote && humanoid.humanoidId == humanoidId) return humanoid; } return null; } public static HumanoidControl FindRemoteHumanoid(List humanoids, int humanoidId) { foreach (HumanoidControl humanoid in humanoids) { if (humanoid == null) continue; if (humanoid.isRemote && humanoid.humanoidId == humanoidId) return humanoid; } return null; } #region Start //public static void Start(PawnNetworking.DebugLevel debug, bool syncFingerSwing) { // HumanoidNetworking.debug = debug; // HumanoidNetworking.syncFingerSwing = syncFingerSwing; //} #endregion #region Start Humanoid //public static HumanoidControl StartHumanoid( // ulong nwId, // int humanoidId, // string name, // string avatarPrefabName, // Vector3 position, Quaternion rotation, // bool physics) { // if (debug <= PawnNetworking.DebugLevel.Info) // UnityEngine.Debug.Log(nwId + ": Receive StartHumanoid " + humanoidId); // HumanoidControl remoteHumanoid = InstantiateRemoteHumanoid(remoteHumanoidPrefab, name, position, rotation); // remoteHumanoid.nwId = nwId; // remoteHumanoid.humanoidId = humanoidId; // if (debug <= PawnNetworking.DebugLevel.Info) // UnityEngine.Debug.Log(remoteHumanoid.nwId + ": Remote Humanoid " + remoteHumanoid.humanoidId + " Added"); // GameObject remoteAvatar = (GameObject)Resources.Load(avatarPrefabName); // if (remoteAvatar == null) { // if (debug <= PawnNetworking.DebugLevel.Error) // UnityEngine.Debug.LogError("Could not load remote avatar " + avatarPrefabName + ". Is it located in a Resources folder?"); // return remoteHumanoid; // } // remoteHumanoid.physics = physics; // remoteHumanoid.LocalChangeAvatar(remoteAvatar); // return remoteHumanoid; //} #endregion #region Pose #region Smoothing public static void SmoothUpdate(List humanoids) { foreach (HumanoidControl humanoid in humanoids) { if (humanoid.isRemote) SmoothUpdate(humanoid); } } public static void SmoothUpdate(HumanoidControl humanoid) { //Debug.Log("smoothing"); humanoid.transform.position += humanoid.velocity * Time.deltaTime; } #endregion #endregion } }