/// <summary>
        /// Manage mesh memory
        /// Converts the marshaled unmanaged mesh memory to managed
        /// </summary>
        public static ManagedMeshData ManageMeshMemory(MeshData meshData)
        {
            ManagedMeshData managedMeshData = new ManagedMeshData();

            managedMeshData.positions = MarshallPositions((int)meshData.positionsNum, meshData.positions);
            managedMeshData.normals   = ((int)meshData.normalsNum > 0) ? MarshallNormals((int)meshData.normalsNum, meshData.normals) : null;

            managedMeshData.materials = new StringCollection();
            managedMeshData.indices   = new List <int[]>();
            var facesetdataSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(FacesetData));

            var currentReadPtr = meshData.facesets;

            for (var i = 0; i < (int)meshData.facesetsNum; ++i)
            {
                FacesetData fs = (FacesetData)System.Runtime.InteropServices.Marshal.PtrToStructure(currentReadPtr, typeof(FacesetData));
                managedMeshData.materials.Add(fs.materialName);
                managedMeshData.indices.Add(MarshallTriangleIndices((int)fs.indicesNum, fs.indices));
                currentReadPtr = MarshalData.AddToIntPtr(currentReadPtr, facesetdataSize);
            }
            managedMeshData.facesetNum = (int)meshData.facesetsNum;

            managedMeshData.uvs = new List <List <Vector2> >();
            currentReadPtr      = meshData.uvSets;
            var uvSetDataSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(UVData));

            for (var i = 0; i < (int)meshData.uvSetNum; ++i)
            {
                UVData uvData = (UVData)System.Runtime.InteropServices.Marshal.PtrToStructure(currentReadPtr, typeof(UVData));
                // Note: Not currently using triangle indices
                //managedMeshData.indices.Add(MarshallTriangleIndices((int)uvData.indicesNum, uvData.indices));
                managedMeshData.uvs.Add(MarshallUVData((int)uvData.uvsNum, uvData.uvs));
                currentReadPtr = MarshalData.AddToIntPtr(currentReadPtr, uvSetDataSize);
            }
            managedMeshData.uvNum = (int)meshData.uvSetNum;

            return(managedMeshData);
        }
        public static void CreateAndSendMeshRequest(Mesh mesh, List <string> materialTokens, string token)
        {
            MeshData meshData = new MeshData();

            meshData.entityToken       = token;
            meshData.entityParentToken = "";
            meshData.displayName       = mesh.name;

            // Add positions, first convert from Unity to STP
            Vector3[] vertices = new Vector3[mesh.vertices.Length];
            System.Array.Copy(mesh.vertices, vertices, mesh.vertices.Length);
            int vertCount = vertices.Length;

            for (int i = vertCount - 1; i >= 0; i--)
            {
                float tempY = vertices[i].y;
                vertices[i].y = -vertices[i].z;
                vertices[i].z = tempY;
                vertices[i].Scale(MarshalData.scaleFromUnitytoSTP);
            }

            System.Runtime.InteropServices.GCHandle pinnedVerticesArray =
                System.Runtime.InteropServices.GCHandle.Alloc(vertices, System.Runtime.InteropServices.GCHandleType.Pinned);
            meshData.positions    = pinnedVerticesArray.AddrOfPinnedObject();
            meshData.positionsNum = (System.UIntPtr)(vertices.Length * verticesDataStride);

            // Add normals
            Vector3[] normals = new Vector3[mesh.normals.Length];
            System.Array.Copy(mesh.normals, normals, mesh.normals.Length);
            int normalCount = mesh.normals.Length;

            for (int i = normalCount - 1; i >= 0; i--)
            {
                float tempY = normals[i].y;
                normals[i].y = -normals[i].z;
                normals[i].z = tempY;
            }

            System.Runtime.InteropServices.GCHandle pinnedNormalsArray =
                System.Runtime.InteropServices.GCHandle.Alloc(normals, System.Runtime.InteropServices.GCHandleType.Pinned);
            meshData.normals    = pinnedNormalsArray.AddrOfPinnedObject();
            meshData.normalsNum = (System.UIntPtr)(normals.Length * normalsDataStride);

            // Add indices
            meshData.facesetsNum = (System.IntPtr)mesh.subMeshCount;
            System.Runtime.InteropServices.GCHandle[] pinnedIndicesArrays = new System.Runtime.InteropServices.GCHandle[(int)meshData.facesetsNum];

            // Add facesets
            FacesetData[] faceSets   = new FacesetData[(int)meshData.facesetsNum];
            int           structSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(FacesetData));

            meshData.facesets = System.Runtime.InteropServices.Marshal.AllocHGlobal((int)meshData.facesetsNum * structSize);
            System.IntPtr facesetsPtr = meshData.facesets;

            // Change the winding order of indices
            int[][] indices     = new int[(int)meshData.facesetsNum][];
            var     materialNum = materialTokens.Count;

            for (var i = 0; i < (int)meshData.facesetsNum; ++i)
            {
                int[] indicesList = mesh.GetTriangles(i);
                indices[i] = new int[indicesList.Length];
                for (var indicePos = 0; indicePos < indicesList.Length; indicePos += indicesStride)
                {
                    indices[i][indicePos + 0] = indicesList[indicePos + 0];
                    indices[i][indicePos + 1] = indicesList[indicePos + 2];
                    indices[i][indicePos + 2] = indicesList[indicePos + 1];
                }

                pinnedIndicesArrays[i] =
                    System.Runtime.InteropServices.GCHandle.Alloc(
                        indices[i],
                        System.Runtime.InteropServices.GCHandleType.Pinned
                        );
                faceSets[i].indices = pinnedIndicesArrays[i].AddrOfPinnedObject();

                // Marshaling needs to use UIntPtr for size_t
                int indicesNum = indicesList.Length;
                //faceSets[i].indicesNum = (System.UIntPtr)indicesNum;
                faceSets[i].indicesNum = (ulong)indicesNum;

                // Marshal material tokens
                if (i < materialNum)
                {
                    faceSets[i].materialName = materialTokens[i];
                }
                else
                {
                    faceSets[i].materialName = "";
                }

                System.Runtime.InteropServices.Marshal.StructureToPtr(faceSets[i], facesetsPtr, false);
                facesetsPtr = (System.IntPtr)((long)facesetsPtr + structSize);
            }

            // Add UVs
            List <Vector2>[] meshUVs = new List <Vector2> [maxUV];
            int uvCount = 0;

            for (int i = 0; i < maxUV; i++)
            {
                meshUVs[i] = new List <Vector2>();
                mesh.GetUVs(i, meshUVs[i]);

                if (meshUVs[i].Count == 0)
                {
                    break;
                }

                uvCount++;
            }

            UVData[]  uvSets    = new UVData[uvCount];
            float[][] uvDataArr = new float[uvCount][];
            structSize        = System.Runtime.InteropServices.Marshal.SizeOf(typeof(UVData));
            meshData.uvSetNum = (System.IntPtr)uvCount;
            meshData.uvSets   = System.Runtime.InteropServices.Marshal.AllocHGlobal(uvCount * structSize);
            System.IntPtr uvSetsPtr = meshData.uvSets;

            System.Runtime.InteropServices.GCHandle[] pinnedUVArrays = new System.Runtime.InteropServices.GCHandle[uvCount];

            for (int i = 0; i < uvCount; i++)
            {
                uvDataArr[i] = new float[meshUVs[i].Count * uvDataStride];

                for (int uvInd = 0; uvInd < meshUVs[i].Count; uvInd++)
                {
                    int uvDataInd = uvInd * uvDataStride;
                    uvDataArr[i][uvDataInd]     = meshUVs[i][uvInd].x;
                    uvDataArr[i][uvDataInd + 1] = 1.0f - meshUVs[i][uvInd].y; // Convert UV from Unity to STP
                }

                // We are not using UV indices due to Unity not supporting them
                uvSets[i].indicesNum = 0;

                pinnedUVArrays[i] =
                    System.Runtime.InteropServices.GCHandle.Alloc(
                        uvDataArr[i],
                        System.Runtime.InteropServices.GCHandleType.Pinned
                        );
                uvSets[i].uvs    = pinnedUVArrays[i].AddrOfPinnedObject();
                uvSets[i].uvsNum = (ulong)uvDataArr[i].Length;

                System.Runtime.InteropServices.Marshal.StructureToPtr(uvSets[i], uvSetsPtr, false);
                uvSetsPtr = (System.IntPtr)((long)uvSetsPtr + structSize);
            }

            System.IntPtr meshDataPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(System.Runtime.InteropServices.Marshal.SizeOf(meshData));
            System.Runtime.InteropServices.Marshal.StructureToPtr(meshData, meshDataPtr, false);

            Debug.LogFormat("Mesh Push - Token:{0}, Parent:{1}, {2} positions, {3} facesets", meshData.entityToken, meshData.entityParentToken, (int)meshData.positionsNum, (int)meshData.facesetsNum);
            ImportMenu.stpUnitySendMeshData(meshDataPtr);

            // Free memory
            pinnedVerticesArray.Free();
            pinnedNormalsArray.Free();

            foreach (var pin in pinnedIndicesArrays)
            {
                pin.Free();
            }

            System.Runtime.InteropServices.Marshal.FreeHGlobal(meshData.facesets);
            System.Runtime.InteropServices.Marshal.FreeHGlobal(meshDataPtr);
        }