Using Mesh.AddSurfaceFromArrays() from C#

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By madgit
:warning: Old Version Published before Godot 3 was released.

Hi,

I’m trying to build procedural geometry from C# in 3.0RC1. I was using SurfaceTool but believe that it might be rather slow for what I’m trying to achieve (a call out every vertex addition), so I’m looking at directly creating the vertex, index etc arrays myself and using Mesh.AddSurfaceFromArrays() instead.

I’m running into confusion around the types of array I should be using. The C# Mesh.AddSurfaceFromArrays() method signature expects an object as the ‘arrays’ parameter, in order that each array contained in it can contain different types (Vector3, int, etc). So I’ve defined my object and it contains a verticesArray as object also and that’s ok.

However at runtime I get the following error:

Condition ’ p_arrays[ai].get_type() != Variant::POOL_VECTOR3_ARRAY ’ is true. returned: ERR_INVALID_PARAMETER

C Source: servers\visual_server.cpp:429
C Function: VisualServer::_surface_set_data

This looks like it’s wanting an array of type PoolVector3Array instead of an object. And I can’t find any definition of this for C#. I have come across this link in the docs:

which seems to imply that the implementation isn’t finished yet for Array etc?

Where am I going wrong, or is it non-functional from C# at the moment? Should my verticesArray be of type Vector3 instead of object? But then I have trouble assigning it “into” the object ‘arrays’ variable as it won’t cast from Vector3 to object. Confusion!

Played a bit more. If I ONLY specify vertices in the array-of-arrays then it “works”, in that I get geometry coming out but obviously with no normals then it’s just flat. If I add normals, or indices, or anything else then I start getting those errors as above about Pool types not being used.

Looked in master visual_server.cpp source at line 429 (as indicated in error above) and it’s checking for the Pool type when normals are specified. Just above in the code (line 380) is where it’s doing vertices, and it DOESN’T check for the type - and works just fine with the Vector3s I’ve passed in as an object.

Same goes for indices (doesn’t work, it’s checking for a Pool type) and so forth.

So - should the checking be removed from everything else just like Vertices? Or should it be added to Vertices, and then somehow the Pool types implemented for C#? (as they don’t exist as far as I can see, making it impossible to use this method atm from C#).

Or maybe I’m a million miles off, in which case I’d really appreciate being pointed at the Right Way To Do Things :slight_smile:

madgit | 2018-01-22 11:58

:bust_in_silhouette: Reply From: etherealmachine

I’m using gdscript and came across the same problem. If I type the arrays as follows it works:

var normal_array = PoolVector3Array()
var uv_array = PoolVector2Array()
var vertex_array = PoolVector3Array()
var index_array = PoolIntArray()
...
var arrays = []
arrays.resize(Mesh.ARRAY_MAX)
arrays[Mesh.ARRAY_VERTEX] = vertex_array
arrays[Mesh.ARRAY_NORMAL] = normal_array
arrays[Mesh.ARRAY_TEX_UV] = uv_array
arrays[Mesh.ARRAY_INDEX] = index_array
:bust_in_silhouette: Reply From: matigramirez

I’m a few years late to the party, but I ran into this issue too and managed to make it work.
Used this GDScript example as reference. Here’s the equivalent C# code:

private int _rings = 50;
private int _radialSegments = 50;
private int _height = 1;
private int _radius = 1;


public override void _Ready()
{
    var surfaceArray = new Array();
    surfaceArray.Resize((int)ArrayMesh.ArrayType.Max);

    var verts = new Array<Vector3>();
    var uvs = new Array<Vector2>();
    var normals = new Array<Vector3>();
    var indices = new Array<int>();

    var currentRow = 0;
    var previousRow = 0;
    var point = 0;

    for (int i = 0; i < _rings + 1; i++)
    {
        var v = i / (float)_rings;
        var w = Mathf.Sin(Mathf.Pi * v);
        var y = Mathf.Cos(Mathf.Pi * v);

        for (int j = 0; j < _radialSegments; j++)
        {
            var u = j / (float)_radialSegments;
            var x = Mathf.Sin(Mathf.Pi * u * 2.0f);
            var z = Mathf.Cos(Mathf.Pi * u * 2.0f);
            var vert = new Vector3(x * _radius * w, y, z * _radius * w);

            verts.Add(vert);
            normals.Add(vert.Normalized());
            uvs.Add(new Vector2(u, v));
            point += 1;

            if (i > 0 && j > 0)
            {
                indices.Add(previousRow + j - 1);
                indices.Add(previousRow + j);
                indices.Add(currentRow + j - 1);

                indices.Add(previousRow + j);
                indices.Add(currentRow + j);
                indices.Add(currentRow + j - 1);
            }
        }

        if (i > 0)
        {
            indices.Add(previousRow + _radialSegments - 1);
            indices.Add(previousRow);
            indices.Add(currentRow + _radialSegments - 1);
            indices.Add(previousRow);
            indices.Add(previousRow + _radialSegments);
            indices.Add(currentRow + _radialSegments - 1);
        }

        previousRow = currentRow;
        currentRow = point;
    }

    surfaceArray[(int)Mesh.ArrayType.Vertex] = verts.ToArray();
    surfaceArray[(int)Mesh.ArrayType.TexUv] = uvs.ToArray();
    surfaceArray[(int)Mesh.ArrayType.Normal] = normals.ToArray();
    surfaceArray[(int)Mesh.ArrayType.Index] = indices.ToArray();

    var arrayMesh = new ArrayMesh();
    arrayMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, surfaceArray);
    Mesh = arrayMesh;
}

Note that Mesh is available in the script because the class where this code segments belongs to inherits from MeshInstance.