Search code examples
c#c++unity-game-enginemarshalling

Update Vector3 array from C++ native plugin


Marshal.Copy() Method only supports a few array types. Now I only know how to copy from IntPtr(pointing to a float array from C++ code) to float[].

IntPtr pvertices = GetVerticesFromCPP();
float[] vertices = new float[nVertices * 3];
Marshal.Copy(pvertices, vertices, 0, nVertices * 3);

But what I really want is a UnityEngine.Vector3[].

Do I need to manually convert float[] to UnityEngine.Vector3[]? Or is there a simpler and faster way that directly do this?


Solution

  • Do I need to manually convert float[] to UnityEngine.Vector3[]? Or is there a simpler and faster way that directly do this?

    Yes, you have to do that manually but there is a better way to do this.

    I will assume that you need to modify a Vector3 on the native side then return the result. There is no need to use float array for this. Just create a Vector3 struct on the C++ side then use pointer to pass it between C++ and C#. Do not return the Vector3 array from C++, create it on the C# side, then pass it to the C++ function to modify and apply the changes to the argument.

    C++:

    This requires that you enable the unsafe keyword in Unity.

    struct Vector3
    {
         float x;
         float y;
         float z;
    };
    

    then your function:

    extern "C" void UpdateVectorArray(Vector3* vecArray, int vecSize)
    {
        for(int i = 0; i < vecSize; ++i)
        {
            //Simply modify each Vector reference
            Vector3 &vec = vecArray[i];
            vec.x = 11;
            vec.y = 20;
            vec.z = 55;
        }
    }
    

    C#:

    [DllImport("Plugin Name")]
    static unsafe extern void UpdateVectorArray(Vector3* vecArray, int vecSize);
    
    
    void UpdateVectorArray(Vector3[] vecArray)
    {
        unsafe
        {
            //Pin array then send to C++
            fixed (Vector3* vecPtr = vecArray)
            {
                UpdateVectorArray(vecPtr, vecArray.Length);
            }
        }
    }
    

    Usage:

    Get vertices from model, send to C++ and modify it the re-assign the modified mesh:

    void Start()
    {
        Mesh mesh = GetComponent<MeshFilter>().mesh;
    
        Vector3[] vertices = mesh.vertices;
        UpdateVectorArray(vertices);
    
        //Re-assign the modified mesh
        mesh.vertices = vertices;
        mesh.RecalculateBounds();
    }
    

    To avoid using the unsafe keyword in Unity use the [In, Out] attribute.

    [DllImport("Plugin Name")]
    static extern void UpdateVectorArray([In, Out] Vector3[] vecArray, int vecSize);
    
    void Start()
    {
        Mesh mesh = GetComponent<MeshFilter>().mesh;
    
        Vector3[] vertices = mesh.vertices;
        UpdateVectorArray(vertices, vertices.Length);
    
        mesh.vertices = vertices;
        mesh.RecalculateBounds();
    }
    

    The C++ side remains the-same. You can also use the GCHandle to pin the array and avoid using the unsafe keyword but the unsafe keyword solution is better and faster.