Combing meshes

Does anyone know how the, Unity - Scripting API: Mesh.CombineMeshes, works? I can’t find it in the core.

When I build some mesh data, two quads…

        m.o.v.Add(new(1 - 0.5f, y + 0.25f, 1 - 0.5f));
        m.o.v.Add(new(1 - 0.5f, y + 0.25f, 1 + 0.5f));
        m.o.v.Add(new(1 + 0.5f, y + 0.25f, 1 + 0.5f));
        m.o.v.Add(new(1 + 0.5f, y + 0.25f, 1 - 0.5f));
        m.o.t.Add(0); m.o.t.Add(1); m.o.t.Add(2); m.o.t.Add(0); m.o.t.Add(2); m.o.t.Add(3);
        m.o.u.Add(new(0,0,0));
        m.o.u.Add(new(0,1,0));
        m.o.u.Add(new(1,1,0));
        m.o.u.Add(new(1,0,0));

        m.o.v.Add(new(2 - 0.5f, y + 0.25f, 2 - 0.5f));
        m.o.v.Add(new(2 - 0.5f, y + 0.25f, 2 + 0.5f));
        m.o.v.Add(new(2 + 0.5f, y + 0.25f, 2 + 0.5f));
        m.o.v.Add(new(2 + 0.5f, y + 0.25f, 2 - 0.5f));
        m.o.t.Add(0); m.o.t.Add(1); m.o.t.Add(2); m.o.t.Add(0); m.o.t.Add(2); m.o.t.Add(3);
        m.o.u.Add(new(0,0,1));
        m.o.u.Add(new(0,1,1));
        m.o.u.Add(new(1,1,1));
        m.o.u.Add(new(1,0,1));

But when I try to do the same thing by combining the vertex, triangle, and UV arrays it doesn’t work.

                Vector3[] verts = new Vector3[_sections[i].m.o.v.Length];
                for(int v1 = 0; v1 < _sections[i].m.o.v.Length; v1++) { verts[v1] = _sections[i].m.o.v[v1]; }

                int[] tris = new int[_sections[i].m.o.t.Length];
                for(int v2 = 0; v2 < _sections[i].m.o.t.Length; v2++) { tris[v2] = _sections[i].m.o.t[v2]; }

                Vector3[] uvs = new Vector3[_sections[i].m.o.u.Length];
                for(int v3 = 0; v3 < _sections[i].m.o.u.Length; v3++) { uvs[v3] = _sections[i].m.o.u[v3]; }

                m.SetVertices(verts);
                m.SetTriangles(tris, 0);
                m.SetUVs(0, uvs);
                list[i].omf.mesh = m;
                list[i].omf.mesh.RecalculateNormals();

The first quad displays but the second one does not. In the inspector I can see it has all the correct vertices, etc, it just doesn’t display.

The reason I need to combine the arrays manually instead of using that CombineMeshes function is because I’m trying to convert it to the job system.

I feel like it’s something stupid I’m missing but I can’t figure it out. If anyone knows how to find the source code for that function then that would point me in the right direction.

All the CombineMeshes stuff I found was a colossal trainwreck… stuff just didn’t work, didn’t handle multiple materials, it was truly amateur hour when I looked into it.

I took one particular script I found and worked with it and got it working at this level:

That repo has a full demo environment and test setup if you wanna try it out. I prefixed my all my hot monkey patches with KurtFixed or and some comments.

MakeGeo is presently hosted at these locations:

https://bitbucket.org/kurtdekker/makegeo

https://gitlab.com/kurtdekker/makegeo

Hey thx for reply Kurt, right after I post for help I figure it out. God I hate programming.

The problem was with my triangles. I needed to add an “index” like so.

public struct _Mesh
{
    public int index;
    public UnsafeList<Vector3> v;
    public UnsafeList<int> t;
    public UnsafeList<Vector3> u;
}

And then when I add a new face to the list I increase the index for however many vertices I just added.

        if (_.map.GetBlock(data.x, data.y+1, data.z) == null) {
            m.o.v.Add(new(x - 0.5f, y + 0.25f, z - 0.5f));
            m.o.v.Add(new(x - 0.5f, y + 0.25f, z + 0.5f));
            m.o.v.Add(new(x + 0.5f, y + 0.25f, z + 0.5f));
            m.o.v.Add(new(x + 0.5f, y + 0.25f, z - 0.5f));
            m.o.t.Add(m.o.index + 0); m.o.t.Add(m.o.index + 1); m.o.t.Add(m.o.index + 2);
            m.o.t.Add(m.o.index + 0); m.o.t.Add(m.o.index + 2); m.o.t.Add(m.o.index + 3); 
            m.o.u.Add(new(0,0,mat));
            m.o.u.Add(new(0,1,mat));
            m.o.u.Add(new(1,1,mat));
            m.o.u.Add(new(1,0,mat));
            m.o.index += 4;
        }

Man… I’ve been messing around with this for about 12 hours now… about to passout. If anyone else is trying to get mesh generation working with the unity jobs system then here is my full code.

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using Unity.VisualScripting;
using UnityEngine;

public struct _Mesh
{
    public int index;
    public UnsafeList<Vector3> v;
    public UnsafeList<int> t;
    public UnsafeList<Vector3> u;
}

public struct _SectionMeshData
{
    public _Mesh o;
    public _Mesh c;
}

public struct Refresh : IJobParallelFor
{
    public NativeArray<MeshBuilder> sections;

    public void Execute(int index)
    {
        var section = sections[index];
        section.Refresh();
        sections[index] = section;
    }

    public static void Schedule(List<Section> list)
    {
        // set all of the sections to busy status 
        foreach(Section s in list) { s.busy = true; }

        // build our refresher array
        var _sections = new NativeArray<MeshBuilder>(list.Count, Allocator.Persistent); 
        for(var i = 0; i < list.Count; i++) { _sections[i] = new MeshBuilder(list[i].data.x, list[i].data.y, list[i].data.z); }

        // schedule the job
        var job = new Refresh() { sections = _sections };
        var handle = job.Schedule(list.Count, 1);
        handle.Complete();

        // update each mesh on each section
        for(var i = 0; i < list.Count; i++) {
            if (_sections[i].m.o.v.Length > 0) {
                GameObject.Destroy(list[i].omf.mesh);
                Mesh m = new Mesh(); 

                Vector3[] verts = new Vector3[_sections[i].m.o.v.Length];
                for(int v1 = 0; v1 < _sections[i].m.o.v.Length; v1++) { verts[v1] = _sections[i].m.o.v[v1]; }

                int[] tris = new int[_sections[i].m.o.t.Length];
                for(int v2 = 0; v2 < _sections[i].m.o.t.Length; v2++) { tris[v2] = _sections[i].m.o.t[v2]; }

                Vector3[] uvs = new Vector3[_sections[i].m.o.u.Length];
                for(int v3 = 0; v3 < _sections[i].m.o.u.Length; v3++) { uvs[v3] = _sections[i].m.o.u[v3]; }

                m.SetVertices(verts);
                m.SetTriangles(tris, 0);
                m.SetUVs(0, uvs);
                list[i].omf.mesh = m;
                list[i].omf.mesh.RecalculateNormals();
            }

            if (_sections[i].m.c.v.Length > 0) {
                GameObject.Destroy(list[i].ocol.sharedMesh);
                Mesh m1 = new Mesh(); 

                Vector3[] verts1 = new Vector3[_sections[i].m.c.v.Length];
                for(int v1 = 0; v1 < _sections[i].m.c.v.Length; v1++) { verts1[v1] = _sections[i].m.c.v[v1]; }

                int[] tris1 = new int[_sections[i].m.o.t.Length];
                for(int v2 = 0; v2 < _sections[i].m.c.t.Length; v2++) { tris1[v2] = _sections[i].m.c.t[v2]; }

                Vector3[] uvs1 = new Vector3[_sections[i].m.o.u.Length];
                for(int v3 = 0; v3 < _sections[i].m.c.u.Length; v3++) { uvs1[v3] = _sections[i].m.c.u[v3]; }

                m1.vertices = verts1;
                m1.triangles = tris1;
                m1.SetUVs(0, uvs1);
                list[i].ocol.sharedMesh = m1;
                list[i].ocol.sharedMesh.RecalculateNormals();
            }
        }

        // cleanup
        foreach(Section s in list) { s.busy = false; }
        foreach(MeshBuilder mb in _sections) { 
            mb.m.o.v.Dispose(); mb.m.o.t.Dispose(); mb.m.o.u.Dispose(); 
            mb.m.c.v.Dispose(); mb.m.c.t.Dispose(); mb.m.c.u.Dispose(); 
        }
        _sections.Dispose();
    }
}

public struct MeshBuilder
{
    public int x, y, z;

    public _SectionMeshData m;

    public MeshBuilder(int x, int y, int z) 
    { 
        this.x = x; this.y = y; this.z = z; 
        this.m = new _SectionMeshData();
        m.o = new _Mesh(); m.o.index = 0;
        m.c = new _Mesh(); m.c.index = 0;
        this.m.o.v = new UnsafeList<Vector3>(0, Allocator.Persistent);
        this.m.o.t = new UnsafeList<int>(0, Allocator.Persistent);
        this.m.o.u = new UnsafeList<Vector3>(0, Allocator.Persistent);
        this.m.c.v = new UnsafeList<Vector3>(0, Allocator.Persistent);
        this.m.c.t = new UnsafeList<int>(0, Allocator.Persistent);
        this.m.c.u = new UnsafeList<Vector3>(0, Allocator.Persistent);
    }

    public void Refresh()
    {
        int x2 = 0; int y2 = 0; int z2 = 0;
        int max = _.map.data.chunkSize * _.map.data.chunkSize * _.map.data.chunkSize;

        for(int i = 0; i < max; i++) {
            if (_.map.GetBlock(x + x2, y + y2, z + z2) != null) { 
                _.map.GetBlock(x + x2, y + y2, z + z2).Build(ref m);
            }
            x2++; if (x2 == _.map.data.chunkSize) { x2 = 0; z2++; } if (z2 == _.map.data.chunkSize) { z2 = 0; y2++; }
        }
    }
}

One area that can be improved is this part.

                Vector3[] verts = new Vector3[_sections[i].m.o.v.Length];
                for(int v1 = 0; v1 < _sections[i].m.o.v.Length; v1++) { verts[v1] = _sections[i].m.o.v[v1]; }

                int[] tris = new int[_sections[i].m.o.t.Length];
                for(int v2 = 0; v2 < _sections[i].m.o.t.Length; v2++) { tris[v2] = _sections[i].m.o.t[v2]; }

                Vector3[] uvs = new Vector3[_sections[i].m.o.u.Length];
                for(int v3 = 0; v3 < _sections[i].m.o.u.Length; v3++) { uvs[v3] = _sections[i].m.o.u[v3]; }

Unity meshes need a Vector3 array for their verts but for unity job system I could only get it working with UnsafeList so after I populate the mesh data I have to convert it to a Vector3 array to assign to the mesh. It’s kinda hacky and stupid, wish there was a way for Mesh to take different data.