(I posted this in Answers yesterday but realised that I’ll probably have more luck here. My apologies if this is bad form!)
I want to procedurally modify a lot of meshes at runtime, and thought I’d look into doing this with the job system.
If I weren’t using the job system, I would store references to the vertex arrays in some collection, and then loop over each array to modify the vertices therein. So my original thought was to simply do this loop in a job. As I’ve come to understand, this isn’t actually possible, since an array of arrays doesn’t count as a blittable type.
I assume this means I’d have to schedule one job per mesh, perhaps storing them in a collection so I could schedule several jobs per update tick. The vertex arrays will probably be quite small, but there’s likely to be a lot of them, and this solution makes me wonder what sort of overhead the actual scheduling and completing of jobs itself might create.
I’ve started looking into ECS but am not yet familiar enough with it to see if it offers a solution.
So… I’m out of my depth here. ^^ Anyone got any advice for an ECS/job system newb?
I’ve posted how to do this a dozen times (not exaggerating).
I’d get you a link but I’m in bed about to head to sleep but you can probably find it easy enough if you look at my post history (a little bit back.) Otherwise I’ll find it tomorrow when I get up.
Thanks for the response! I did do a bunch of searching but didn’t find anyone discussing the angle of having multiple meshes. I did find one thread where you posted an extension making the actual copying more effective, which I made a note to return to when I get that far
I haven’t tried for performance yet but I think you can schedule a whole bunch of jobs and combine all dependencies with JobHandle.CombineDependencies(NativeArray).
Theoretically you could have a regular array of NativeArray (for the vertices) outside the jobs, then run a job for each NativeArray and wait on the combined handle for completion.
So pretty much I believe the best approach is to make each mesh an entity and store each mesh component (vertices, uvs, normals) etc as their own BufferArray.
This way you can just use a single job and not worry about combining dependencies.
I got a few minutes I might put up a demo on exactly what I’m talking about.
There’s no real reason to ever use a ISharedComponentData for this, it just causes huge issues.
It might look like the meshes are just moving up and down but it’s really just manipulating the mesh y vertices with some random dodgy job I threw together.
/// <summary>
/// Just some random mesh manipulation. Frame dependent, don't turn off sync.
/// </summary>
[BurstCompile]
[RequireComponentTag(typeof(Vertex))]
private struct ManipulateMeshJob : IJobProcessComponentDataWithEntity<MeshScale>
{
[NativeDisableParallelForRestriction]
public BufferFromEntity<Vertex> Vertices;
public NativeQueue<MeshDirty>.Concurrent MeshDirtyQueue;
/// <inheritdoc />
public void Execute(Entity entity, int index, ref MeshScale meshScale)
{
this.MeshDirtyQueue.Enqueue(new MeshDirty{Entity = entity});
meshScale.Count += 1;
if (meshScale.Count == meshScale.Max)
{
meshScale.Count = -meshScale.Max;
}
var vertices = this.Vertices[entity].Reinterpret<float3>();
var offset = meshScale.Step;
if (meshScale.Count < 0)
{
offset = -offset;
}
for (var i = 0; i < vertices.Length; i++)
{
var v = vertices[i];
v.y += offset;
vertices[i] = v;
}
}
}
A few notes
uses my batch system to create events to notify of mesh changes, this is just how I do things and is completely optionally. Usually I don’t manipulate meshes every frame (unlike this example) as they are only occasionally modified hence the choice to use events.
uses float3 for uv (instead of float2) because my actual project uses texture2darray which needs the 3rd uv.
the random mesh manipulation is frame dependent, i wouldn’t turn off sync. just threw together something quick to show mesh manipulations.
the actual MeshSystem supports both MeshInstanceRenderer as well as traditional MeshRenderer component if that is what is attached to entity.
the block model is structured very strangely for a traditional cube - this is because it’s organized in a very particular way for my greedy meshing optimizations for another project. I just copied it in.
Thank you! This is very helpful. Given that I haven’t had the time to look into ECS yet I can’t say I understood all of that, but I’ll look into it for sure.
Sorry to necro. @tertle , do you remember why you used GetArchetypeChunkComponentType and iterate over chunks?
I’m thinking Entities.ForEach(Entity e, ref MeshDirty md) might be simpler.
Well to start with Entities.ForEach didn’t exist back then
That said, not a huge fan of Entities.ForEach - componentsystem version has always been an avoid, jobcomponentsystem version has potential and when polished will be good to use but for now we’ve found way too many issues with it to be worth using until it’s fixed.