Hi all,
I believe this is an interesting problem that was solved in a probably not very efficient way, and I was hoping to get some advice on optimization.
My HoloLens app needs to retrieve the mesh of the surroundings within a certain radius and send it to the server as a string. I know strings aren’t very efficient but this is just how TCP/IP communication was built and I can’t change it now.
HoloLens creates the mesh of the surroundings in hundreds of small pieces with about 500 faces each. Within Unity, I retrieve all these small pieces and create 4 strings (representing vertices, triangles, and their total numbers) formatted to follow the structure of a .ply file format. Strings are sent to the server which adds a header, saves the .ply (one large mesh created from hundreds of small pieces) and passes it to other functions.
The way I wrote this works, but is quite slow and can take more than 6 seconds with about 50 000 faces. I tried gathering all vertices and triangles of these small pieces before converting them to a string, but lists are very slow and I can’t use arrays as I don’t know the final size.
I was hoping to get some optimization suggestions! Thanks in advance
The relevant code is below:
//Strings to hold vertices, triangle (faces) and their numbers. To be sent to the server.
private string verticesString;
private string trianglesString;
private string verticesNumString;
private string trianglesNumString;
GameObject boundary;
private void Start()
{
boundary = GameObject.FindGameObjectWithTag("Boundary");
}
public void RetrieveAndSerializeMesh()
{
var observer = CoreServices.GetSpatialAwarenessSystemDataProvider<IMixedRealitySpatialAwarenessMeshObserver>();
//As one large mesh is created from dozens of small pieces, vertex indices that form triangles must be corrected
int vertexIndexCorrection = 0;
int verticesNum = 0;
int trianglesNum = 0;
//Only mesh pieces within this boundary are retrieved
Bounds boundaryBounds = boundary.GetComponent<MeshRenderer>().bounds;
foreach (SpatialAwarenessMeshObject meshObject in observer.Meshes.Values)
{
Mesh mesh = meshObject.Filter.mesh;
if (boundaryBounds.Contains(mesh.bounds.center))
{
//Transform of the GO is needed for conversion from local to world coordinates of mesh vertices.
Transform tf = meshObject.Filter.GetComponent<Transform>();
verticesString += SerializeVector3Array(mesh.vertices, tf);
trianglesString += SerializeIntArray(mesh.triangles, vertexIndexCorrection);
vertexIndexCorrection += mesh.vertexCount;
verticesNum += mesh.vertexCount;
trianglesNum += (mesh.triangles.Length / 3);
}
}
}
//Modified from https://answers.unity.com/questions/1030082/convert-vector-3-to-string-and-reverse-c.html
//Convert a Vector3 array to string.
public static string SerializeVector3Array(Vector3[] aVectors, Transform tf)
{
StringBuilder sb = new StringBuilder();
foreach (Vector3 v in aVectors)
{
Vector3 vt = tf.TransformPoint(v);
sb.Append(vt.x).Append(" ").Append(vt.y).Append(" ").Append(vt.z).Append("\n");
}
return sb.ToString();
}
//Convert an int array of mesh triangles to a string in the format of .ply.
public static string SerializeIntArray(int[] ints, int indexCorrection)
{
StringBuilder sb = new StringBuilder();
int len = ints.Length;
int i = 0;
while (i < len)
{
sb.Append("3 ").Append(ints[i] + indexCorrection).Append(" ").Append(ints[i + 1] + indexCorrection).Append(" ").Append(ints[i + 2] + indexCorrection).Append("\n");
i += 3;
}
return sb.ToString();
}
EDIT: Code after implementing suggestions from @Bunny83
//Strings to hold vertices, triangle (faces) and their numbers. To be sent to the server.
private string verticesString;
private string trianglesString;
private string verticesNumString;
private string trianglesNumString;
private StringBuilder sbVertices = new StringBuilder();
private StringBuilder sbTriangles = new StringBuilder();
private List<Vector3> listVertices = new List<Vector3>();
private List<int> listTriangles = new List<int>();
GameObject boundary;
private void Start()
{
boundary = GameObject.FindGameObjectWithTag("Boundary");
}
public void RetrieveAndSerializeMesh()
{
var observer = CoreServices.GetSpatialAwarenessSystemDataProvider<IMixedRealitySpatialAwarenessMeshObserver>();
int verticesNum = 0;
int trianglesNum = 0;
//Only mesh pieces within this boundary are retrieved
Bounds boundaryBounds = boundary.GetComponent<MeshRenderer>().bounds;
foreach (SpatialAwarenessMeshObject meshObject in observer.Meshes.Values)
{
Mesh mesh = meshObject.Filter.mesh;
if (boundaryBounds.Contains(mesh.bounds.center))
{
//Transform of the GO is needed for conversion from local to world coordinates of mesh vertices.
Transform tf = meshObject.Filter.GetComponent<Transform>();
listVertices.Clear();
listTriangles.Clear();
mesh.GetVertices(listVertices);
mesh.GetTriangles(listTriangles, 0);
SerializeVector3Array(sbVertices, listVertices, tf);
SerializeIntArray(sbTriangles, listTriangles, verticesNum);
verticesNum += listVertices.Count;
trianglesNum += (listTriangles.Count / 3);
}
}
//After the string streams are built convert them to strings to be sent to the server.
verticesString = sbVertices.ToString();
trianglesString = sbTriangles.ToString();
verticesNumString = verticesNum.ToString();
trianglesNumString = trianglesNum.ToString();
}
//Modified from https://answers.unity.com/questions/1030082/convert-vector-3-to-string-and-reverse-c.html
//Convert a Vector3 array to string.
public static void SerializeVector3Array(StringBuilder sb, List<Vector3> aVectors, Transform tf)
{
foreach (Vector3 v in aVectors)
{
Vector3 vt = tf.TransformPoint(v);
sb.Append(vt.x).Append(" ").Append(vt.y).Append(" ").Append(vt.z).Append("\n");
}
}
//Convert an int array of mesh triangles to a string in the format of .ply.
public static void SerializeIntArray(StringBuilder sb, List<int> ints, int indexCorrection)
{
int len = ints.Count;
int i = 0;
while (i < len)
{
sb.Append("3 ").Append(ints[i] + indexCorrection).Append(" ").Append(ints[i + 1] + indexCorrection).Append(" ").Append(ints[i + 2] + indexCorrection).Append("\n");
i += 3;
}
}