/// <summary>
    /// Instantiates a mesh object inside cpp plugin and stores
    /// a reference to it in this class
    /// </summary>
    private void SendSkinnedMesh()
    {
        // The plugin will want to modify the vertex buffer -- on many platforms
        // for that to work we have to mark mesh as "dynamic" (which makes the buffers CPU writable --
        // by default they are immutable and only GPU-readable).
        mesh.MarkDynamic();

        var vertices      = mesh.vertices;                      // Vector3[]
        var faces         = mesh.triangles.Clone() as int[];    // int[]
        var nativeWeights = mesh.GetAllBoneWeights();           // NativeArray<BoneWeight1>
        var boneCounts    = mesh.GetBonesPerVertex().ToArray(); // byte[]

        Debug.Assert(boneCounts.Length == vertices.Length, "Bone per vertex array has different length from vertex array");

        // copy because original struct is not interop-friendly
        int largestBoneIndex = 0;

        Bone[] weights = new Bone[nativeWeights.Length];
        for (int i = 0; i < weights.Length; i++)
        {
            var native = nativeWeights[i];
            weights[i] = new Bone(native.boneIndex, native.weight);

            largestBoneIndex = native.boneIndex > largestBoneIndex
                ? native.boneIndex : largestBoneIndex;
        }
        Debug.Log($"Largest bone index in mesh {mesh.name}: {largestBoneIndex}");

        GCHandle gcVertices   = GCHandle.Alloc(vertices, GCHandleType.Pinned);
        GCHandle gcFaces      = GCHandle.Alloc(faces, GCHandleType.Pinned);
        GCHandle gcWeights    = GCHandle.Alloc(weights, GCHandleType.Pinned);
        GCHandle gcBoneCounts = GCHandle.Alloc(boneCounts, GCHandleType.Pinned);

        var cppMesh = NativeInterface.CreateMesh(gcVertices.AddrOfPinnedObject(), mesh.vertexCount,
                                                 gcFaces.AddrOfPinnedObject(), faces.Length / 3,
                                                 gcWeights.AddrOfPinnedObject(), gcBoneCounts.AddrOfPinnedObject(),
                                                 largestBoneIndex + 1);

        gcVertices.Free();
        gcFaces.Free();
        gcWeights.Free();
        gcBoneCounts.Free();

        string errorMessage = ExtractFailureMessage(NativeInterface.HasFailedMeshConstruction(cppMesh));

        if (errorMessage.Equals(""))
        {
            this._cppMesh = cppMesh;
        }
        else
        {
            NativeInterface.DestroyMesh(cppMesh);
            throw new Exception(errorMessage);
        }

        // send pointer to the mesh buffer
        // if (mesh.vertexBufferCount > 1)
        //     Debug.LogWarning("There are more than one vertex buffer: " + mesh.vertexBufferCount);
        // NativeInterface.SetMeshVertexBuffer(this._cppMesh, mesh.GetNativeVertexBufferPtr(0));
    }