Procedural terrain meshes in DOTS?

Hi! I have a simple terrain generation system based on “jobified” monobehaviours. The generation of the terrain (static unique chunks, not voxels, that cannot be modified by the user) is already based on a set of jobs which build the mesh, generate the noise, apply various filters, etc. I was looking into DOTS and I looked at a lot of prior threads. Some of the referenced github examples, however, were implemented with previous version of DOTS and might be no longer relevant.

Some of these were using very “arcane” implementations (at least to a person not used to DOTS) such as using dynamic / command buffers etc. My naive implementation would consist in creating a TerrainMesh component with NativeArrays for the vertices, indices, UVs, etc. Then in the OnCreate method I would basically run the IJobParallelFor I already implemented in the monobehaviour implementation. Then a RenderMesh component would be used to render the generated unique mesh.

Would that work? Would I see any benefit over my current MB approach? If not, what should I look into? What would be the best architecture / design pattern to follow that is up to date for DOTS 2020? Again, this would not be for voxels, just meshes based on noise-generated heightmaps.

I’ve currently got a terrain generation system going using a process like you mention. I have one system that deals with generating the chunks and their visibility. Another system then uses layered noise to generate height data and stores it in a DynamicBuffer. I then have two more systems, one to generate the mesh, and another to generate the mesh colliders.

I am however dealing with voxel data, so I do a couple more steps in between, but the overall process should be the same. I’m also using a ComputeShader to generate the mesh as I use marching cubes.

From what I’ve read, an ECS approach won’t be much better than your current way of doing things, since all of your terrain meshes will be different, and as such each terrain chunk will be stored in a different entity chunk. The main advantage of a pure ECS approach is if you can have lots of entities in the same entity chunk, so that they’re together in memory (or so I believe. I’m still learning about DOTS). I wrote mine the way I did because I wasn’t using jobs in the first place, and my goal is to have as much of my code in the DOTS stack as possible.

I’d love to share parts of my code if you’re interest, as I’m always welcome for feedback and ways I can improve what I’ve written.

I’ll be working with terrain soon, but have yet to prototype. A theoretical work around is to store height maps in a texture array, use a unique index per terrain, and a single shared mesh for all terrains. In theory the terrains will be instanced and fill ECS chunks properly. They may be split into separate chunks again because of physics colliders though. Haven’t thought of a way around that yet.

If you using only heightmap based terrain, then you could possibly also use a copy of an pre-defined mesh and only change the height using the new Mesh API see GitHub - Unity-Technologies/MeshApiExamples: Example project for Unity 2020.1 Mesh API improvements

1 Like

My approach in procedural hex sphere planet generation, using DOTS, in brief is following.

Create Template hex and pentagon 3D tile mesh.
Create NativeArrays of of vertices, UV, Normals, vertexColors as persistent.
Populate arrays using jobs and burst, with relevant calculations, of verts positions.
Using Graphics.DrawMeshInstanced to render.
I combine multiple tiles (2k+) per mesh.

On ECS side:
Storing height and tile type in entities. Also, reference (starting index) to corresponding verts indices as per array.
Using Perlin Noises to generate terrain variations.
Apply heights to entities.
Update verts and vertexColor arrays in parallel.
And finally update arrays to actual mesh(es).
It is important to use Set methods

newMesh.SetVertices(vertices);

not assignments

newMesh.vertices = vertices ;

otherwise generating GC and is much slower.

In this case, whole small planet, is a single mesh

3 Likes

I’m currently using the Mesh.MeshData class for updating the meshes which is part of the Mesh API mentioned above. So I can do Mesh.ApplyAndDisposeWritableMeshData(meshDataArray, meshes);

That’s actually been really useful in my various jobs.

SetVertices() takes NativeArrays which I use for my own generator.

Is it possible(fast enough using mb/jobs/burst or dots ) to create Earth-size planet using marching cubes algorithm - or only part of planet we are closest and the rest of the planet create using classic approach(simple cube we deform to sphere)? When we use cube deformed to sphere it is harder to create things like caves or stone-bridges formations for example and with marching cubes it is a lot easier…

Not sure where I read it first, but thanks to everyone pointing out the new MeshData API - I missed it, and unknowingly waited for it :smile: