Meshes made by scripts

Since SDK 0.2.1

A script makes a mesh with new Mesh(), as in Unity, fills it with vertices and indices, and gives it to a MeshFilter to be seen and to a MeshCollider to be touched. A script can also change a mesh of the scene. The API is Unity's: SetVertices, SetNormals, SetTangents, SetColors, SetUVs (channels 0 to 7), SetTriangles and SetIndices per submesh, subMeshCount, indexFormat, RecalculateNormals, RecalculateTangents, RecalculateBounds, Clear, MarkDynamic, and the same data read back (vertices, GetVertices, GetIndices…).

Meshes exist on the players' devices only: the server has no rendering and no physics, and a script that makes a mesh there gets an error.

Make a mesh

csharp
using NodeWorld.Flux;

public class Ramp : FluxScript
{
    protected override void Start()
    {
        var mesh = new Mesh();
        mesh.SetVertices(new[] { new Vector3(0, 0, 0), new Vector3(1, 0, 0), new Vector3(0, 0.5f, 1), new Vector3(1, 0.5f, 1) });
        mesh.SetUVs(0, new[] { new Vector2(0, 0), new Vector2(1, 0), new Vector2(0, 1), new Vector2(1, 1) });
        mesh.SetTriangles(new[] { 0, 2, 1, 2, 3, 1 }, 0);
        mesh.RecalculateNormals();

        GetComponent<MeshFilter>().sharedMesh = mesh;      // seen
        var collider = GetComponent<MeshCollider>();
        collider.sharedMesh = mesh;                        // touched: the physics takes its shape now
    }
}
  • MeshFilter.sharedMesh gives the mesh to the filter. MeshFilter.mesh gives the filter's own mesh: the first read makes a copy of its shared mesh for that filter alone, as in Unity.
  • MeshCollider.sharedMesh gives the shape to the collider. Set convex = true for a mesh collider on a moving rigidbody or one that must touch another mesh collider.
  • Set indexFormat = IndexFormat.UInt32 before giving more than 65 535 vertices.

Change it every frame

The arrays you give stay in the memory of the world: the game reads them there, without a copy, and hands them to Unity. Keep your arrays, change them, and give them again:

csharp
protected override void Update()
{
    for (var i = 0; i < _vertices.Length; i++)
        _vertices[i].y = 0.2f * Mathf.Sin(_vertices[i].x * 2f + Time.time);
    _mesh.SetVertices(_vertices);
    _mesh.RecalculateNormals();
    _mesh.RecalculateBounds();
}

Call MarkDynamic() once on a mesh that changes often. A thread of your script (Parallel.For, Task.Run) may fill the arrays; the calls that change the mesh (SetVertices…) are made on the main thread.

Vertex and index buffers

For a layout of your own (several attributes in one stream, 16-bit floats…), use Unity's advanced API: SetVertexBufferParams with VertexAttributeDescriptors, SetVertexBufferData with an array of your vertex struct, SetIndexBufferParams, SetIndexBufferData and SetSubMesh with a SubMeshDescriptor. The game lays a NativeArray on your arrays and gives it to Mesh.SetVertexBufferData and Mesh.SetIndexBufferData.

Lifetime

A mesh made by a script belongs to its world: it is destroyed when the world unloads, or earlier by Object.Destroy(mesh). A mesh of the scene changed by a script changes for every renderer of the world that uses it.