diff --git a/src/SpectatorView.Unity/Assets/SpectatorView/Scripts/StateSynchronization/AssetService.cs b/src/SpectatorView.Unity/Assets/SpectatorView/Scripts/StateSynchronization/AssetService.cs index 20c6d98a8..40cdea6d3 100644 --- a/src/SpectatorView.Unity/Assets/SpectatorView/Scripts/StateSynchronization/AssetService.cs +++ b/src/SpectatorView.Unity/Assets/SpectatorView/Scripts/StateSynchronization/AssetService.cs @@ -103,6 +103,14 @@ public bool AttachMeshFilter(GameObject gameObject, AssetId assetId) return false; } + public bool AttachDynamicMeshFilter(GameObject gameObject, AssetId assetId, Mesh mesh) + { + ComponentExtensions.EnsureComponent(gameObject); + MeshFilter filter = ComponentExtensions.EnsureComponent(gameObject); + filter.sharedMesh = mesh; + return true; + } + public bool AttachSkinnedMeshRenderer(GameObject gameObject, AssetId assetId) { Mesh mesh = meshAssets.GetAsset(assetId); diff --git a/src/SpectatorView.Unity/Assets/SpectatorView/Scripts/StateSynchronization/NetworkedComponents/MeshFilter/MeshFilterBroadcaster.cs b/src/SpectatorView.Unity/Assets/SpectatorView/Scripts/StateSynchronization/NetworkedComponents/MeshFilter/MeshFilterBroadcaster.cs index 36d12f93b..1019448e6 100644 --- a/src/SpectatorView.Unity/Assets/SpectatorView/Scripts/StateSynchronization/NetworkedComponents/MeshFilter/MeshFilterBroadcaster.cs +++ b/src/SpectatorView.Unity/Assets/SpectatorView/Scripts/StateSynchronization/NetworkedComponents/MeshFilter/MeshFilterBroadcaster.cs @@ -9,6 +9,10 @@ namespace Microsoft.MixedReality.SpectatorView { internal class MeshFilterBroadcaster : MeshRendererBroadcaster { + // A unique integer value used to check that mesh data is correctly written and read. + // TODO: After some time, if this is working reliably, this check could be removed. + public const int CheckValue = 123454321; + public static class MeshFilterChangeType { public const byte Mesh = 0x8; @@ -27,11 +31,42 @@ protected override void WriteRenderer(BinaryWriter message, byte changeType) if (HasFlag(changeType, MeshFilterChangeType.Mesh)) { message.Write(assetId); + + if (assetId == AssetId.Empty) + { + // This is a dynamic object that wasn't created from an asset. + bool hasDynamicMesh = meshFilter.sharedMesh != null; + message.Write(hasDynamicMesh); + if (hasDynamicMesh) + { + WriteDynamicMesh(message, meshFilter.sharedMesh); + } + } } base.WriteRenderer(message, changeType); } + private void WriteDynamicMesh(BinaryWriter message, Mesh mesh) + { + message.Write(CheckValue); + message.Write(mesh.subMeshCount); + message.Write(mesh.vertices); + message.Write(mesh.uv); + message.Write(mesh.uv2); + message.Write(mesh.uv3); + message.Write(mesh.uv4); + message.Write(mesh.uv5); + message.Write(mesh.uv6); + message.Write(mesh.uv7); + message.Write(mesh.uv8); + message.Write(mesh.colors); + message.Write(mesh.triangles); + message.Write(CheckValue); + + Debug.Log($"MeshFilterBroadcaster.WriteDynamicMesh: {gameObject.name} written with {mesh.subMeshCount} subMeshCount, {mesh.vertices?.Length} vertices, {mesh.triangles?.Length} triangles"); + } + protected override void OnInitialized() { base.OnInitialized(); diff --git a/src/SpectatorView.Unity/Assets/SpectatorView/Scripts/StateSynchronization/NetworkedComponents/MeshFilter/MeshFilterObserver.cs b/src/SpectatorView.Unity/Assets/SpectatorView/Scripts/StateSynchronization/NetworkedComponents/MeshFilter/MeshFilterObserver.cs index 91504061d..6af9e4a4d 100644 --- a/src/SpectatorView.Unity/Assets/SpectatorView/Scripts/StateSynchronization/NetworkedComponents/MeshFilter/MeshFilterObserver.cs +++ b/src/SpectatorView.Unity/Assets/SpectatorView/Scripts/StateSynchronization/NetworkedComponents/MeshFilter/MeshFilterObserver.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using UnityEngine; namespace Microsoft.MixedReality.SpectatorView { @@ -13,10 +14,55 @@ protected override void EnsureRenderer(BinaryReader message, byte changeType) if (MeshFilterBroadcaster.HasFlag(changeType, MeshFilterBroadcaster.MeshFilterChangeType.Mesh)) { AssetId assetId = message.ReadAssetId(); - AssetService.Instance.AttachMeshFilter(this.gameObject, assetId); + if (assetId != AssetId.Empty) + { + AssetService.Instance.AttachMeshFilter(this.gameObject, assetId); + } + else + { + bool hasDynamicMesh = message.ReadBoolean(); + if (hasDynamicMesh) + { + Mesh mesh = ReadDynamicMesh(message); + AssetService.Instance.AttachDynamicMeshFilter(this.gameObject, assetId, mesh); + } + } } base.EnsureRenderer(message, changeType); } + + private Mesh ReadDynamicMesh(BinaryReader message) + { + int checkValue = message.ReadInt32(); + if (checkValue != MeshFilterBroadcaster.CheckValue) + { + Debug.LogError($"MeshFilterObserver.ReadDynamicMesh: {gameObject.name} initial checkValue mismatch! All subsequent spectator view data will read incorrectly!"); + } + + Mesh mesh = new Mesh(); + mesh.subMeshCount = message.ReadInt32(); + mesh.vertices = message.ReadVector3Array(); + mesh.uv = message.ReadVector2Array(); + mesh.uv2 = message.ReadVector2Array(); + mesh.uv3 = message.ReadVector2Array(); + mesh.uv4 = message.ReadVector2Array(); + mesh.uv5 = message.ReadVector2Array(); + mesh.uv6 = message.ReadVector2Array(); + mesh.uv7 = message.ReadVector2Array(); + mesh.uv8 = message.ReadVector2Array(); + mesh.colors = message.ReadColorArray(); + mesh.triangles = message.ReadInt32Array(); + mesh.RecalculateNormals(); + + checkValue = message.ReadInt32(); + if (checkValue != MeshFilterBroadcaster.CheckValue) + { + Debug.LogError($"MeshFilterObserver.ReadDynamicMesh: {gameObject.name} final checkValue mismatch! All subsequent spectator view data will read incorrectly!"); + } + + Debug.Log($"MeshFilterObserver.ReadDynamicMesh: {gameObject.name} read with {mesh.subMeshCount} subMeshCount, {mesh.vertices?.Length} vertices, {mesh.triangles?.Length} triangles"); + return mesh; + } } } \ No newline at end of file