Skip to content
This repository has been archived by the owner on Jun 14, 2024. It is now read-only.

DRAFT: broadcast a dynamically-generated mesh #396

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,14 @@ public bool AttachMeshFilter(GameObject gameObject, AssetId assetId)
return false;
}

public bool AttachDynamicMeshFilter(GameObject gameObject, AssetId assetId, Mesh mesh)
{
ComponentExtensions.EnsureComponent<MeshRenderer>(gameObject);
MeshFilter filter = ComponentExtensions.EnsureComponent<MeshFilter>(gameObject);
filter.sharedMesh = mesh;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may need to be filter.mesh. It looks like filter.sharedMesh isn't suggested for writing meshes.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find Unity's MeshFilter.mesh/sharedMesh confusing, so I'm not sure. I think the warning about writing is if you modify a mesh using filter.sharedMesh. For example, if you do filter.sharedMesh.vertices = new Vector3[] then you could be inadvertently modifying a mesh that is shared by other objects. In this case, we are not modifying the existing sharedMesh but instead are first creating a Mesh and then assigning that mesh to filter.sharedMesh.

Not sure if I'm making much sense here, but I'm not sure what the right way is to do this or if it even matters which property is used in this case. I expect that assigning to filter.mesh would also be fine.

return true;
}

public bool AttachSkinnedMeshRenderer(GameObject gameObject, AssetId assetId)
{
Mesh mesh = meshAssets.GetAsset(assetId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ namespace Microsoft.MixedReality.SpectatorView
{
internal class MeshFilterBroadcaster : MeshRendererBroadcaster<MeshFilterService>
{
// 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;
Expand All @@ -27,11 +31,42 @@ protected override void WriteRenderer(BinaryWriter message, byte changeType)
if (HasFlag(changeType, MeshFilterChangeType.Mesh))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tested whether this flag gets set when someone modifies the meshFilter.mesh on update/start?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. I have verified that this worked with dynamically-generated meshes in my project, but I didn't take a close look at exactly how the dynamic meshes were created.

I haven't tested changing a mesh after it has already been broadcast and I expect that changes after broadcast would not be detected.

{
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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.IO;
using UnityEngine;

namespace Microsoft.MixedReality.SpectatorView
{
Expand All @@ -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;
}
}
}