Rendering Entities

Hi everyone,

I am creating a procedural terrain script in ECS, but having trouble altering render components. This code has no compile/unity errors.

Any help is greatly appreciated :slight_smile:

using Unity.Burst;
using Unity.Entities;
using UnityEngine;
using Unity.Entities.Graphics;
using Unity.Rendering;
using Unity.Collections;
using Unity.Transforms;
using Unity.Mathematics;
using NUnit.Framework.Interfaces;

public class HeightMapper : MonoBehaviour
{
    public int ChunkSize;
    public Color col;
    public GameObject prefab;
}

class HeightMapperBaker : Baker<HeightMapper>
{
    public override void Bake(HeightMapper authoring)
    {
        Entity Spawner = GetEntity(TransformUsageFlags.None);

        AddComponent(Spawner, new ChunkSpawnerData
        {
            Test = GetEntity(authoring.prefab, TransformUsageFlags.Dynamic),
            ChunkSize = authoring.ChunkSize,
            Colour = authoring.col
        }); ;
        Debug.Log("Baked");
    }
}

public struct ChunkSpawnerData : IComponentData
{
    public int ChunkSize;
    public Color Colour;
    public Entity Test;
}

public struct ChunkData : IComponentData
{
    public Vector2 Position;
}

[BurstCompile]
public partial class GridSpawner : SystemBase
{
    int ChunkSize;
    bool Setup;
    public EntityManager EM;
    EntityArchetype Arc;
    public Material Mat;
    RenderMeshArray RMA;


    [BurstCompile]
    protected override void OnCreate()
    {
        base.OnCreate();
        EM = EntityManager.World.EntityManager;
        Arc = EM.CreateArchetype(typeof(ChunkData), typeof(RenderMesh), typeof(LocalTransform), typeof(RenderBounds), typeof(LocalToWorld));
    }

    [BurstCompile]
    protected override void OnUpdate()
    {
        if (!SystemAPI.TryGetSingletonEntity<ChunkSpawnerData>(out Entity Spawner)) return;
        if (!Setup) SetupVariables2(Spawner);
        Log("Updated with singleton");
    }

    [BurstCompile]
    void SetupVariables(Entity SpawnerEntity)
    {
        Setup = true;
        EM = EntityManager.World.EntityManager;
        ChunkSpawnerData Spawner = SystemAPI.GetComponent<ChunkSpawnerData>(SpawnerEntity);
        ChunkSize = Spawner.ChunkSize;
        Log(ChunkSize.ToString());
        EntityCommandBuffer ECB = new EntityCommandBuffer(Allocator.TempJob);
        Entity Chunk = ECB.CreateEntity(Arc);
        Material ObjMat = BaseMat(Spawner);

        Mesh PlaneMesh = CreatePlaneMesh();

        RenderMesh ObjectRenderer = new RenderMesh()
        {
            mesh = PlaneMesh,
            material = ObjMat
        };
        RenderBounds ObjectRendererBounds = new RenderBounds()
        {
            Value = PlaneMesh.bounds.ToAABB()
        };



        LocalTransform Transform = new LocalTransform()
        {
            Position = float3.zero,
            Rotation = quaternion.EulerXYZ(0, 0, 0),
            Scale = 1
        };
        ChunkData ThisChunkData = new ChunkData()
        {
            Position = Vector2.zero
        };
        LocalToWorld LTW = new LocalToWorld()
        {
            Value = Transform.ToMatrix()
        };
        ECB.SetSharedComponentManaged(Chunk, ObjectRenderer);
        ECB.SetComponent(Chunk, ObjectRendererBounds);
        ECB.SetComponent(Chunk, Transform);
        ECB.SetComponent(Chunk, ThisChunkData);
        ECB.SetComponent(Chunk, LTW);
        ECB.AddComponent(Chunk, new Simulate());
        ECB.SetName(Chunk, "NEWCHUNK");
        ECB.Playback(EM);
        ECB.Dispose();
    }

    [BurstCompile]
    void SetupVariables2(Entity SpawnerEntity)
    {
        Setup = true;
        ChunkSpawnerData Spawner = SystemAPI.GetComponent<ChunkSpawnerData>(SpawnerEntity);
        ChunkSize = Spawner.ChunkSize;
        Log(ChunkSize.ToString());
        Entity Sample = EM.Instantiate(Spawner.Test);
        RMA = EM.GetSharedComponentManaged<RenderMeshArray>(Sample);
        EntityCommandBuffer ECB = new EntityCommandBuffer(Allocator.TempJob);
        Material Mat = BaseMat(Spawner);
        Mesh ObjectMesh = CreatePlaneMesh();
        Entity Chunk = ECB.Instantiate(Spawner.Test);

        RMA.Meshes = new Mesh[] { ObjectMesh };

        ECB.SetName(Chunk, "NEWCHUNK");
        ECB.SetSharedComponentManaged(Chunk, RMA);
        ECB.Playback(EM);
        ECB.Dispose();
    }

    [BurstCompile]
    Mesh CreatePlaneMesh()
    {
        Mesh Result = new Mesh();
        float PlaneSize = 10;
        Vector3[] vertices = {
            new Vector3(-0.5f, 0, -0.5f) * PlaneSize,
            new Vector3(0.5f, 0, -0.5f) * PlaneSize,
            new Vector3(-0.5f, 0, 0.5f) * PlaneSize,
            new Vector3(0.5f, 0, 0.5f) * PlaneSize
        };

        int[] triangles = {
            0, 1, 2,
            1, 3, 2
        };

        Vector2[] uvs = new Vector2[vertices.Length];
        for (int i = 0; i < uvs.Length; i++)
        {
            uvs[i] = new Vector2(vertices[i].x + 0.5f, vertices[i].z + 0.5f);
        }

        Result.vertices = vertices;
        Result.triangles = triangles;
        Result.uv = uvs;
        Result.RecalculateNormals();

        return Result;
    }

    Material BaseMat(ChunkSpawnerData Spawner)
    {
        Material DefaultMat = new Material(Shader.Find("HDRP/Lit"));
        DefaultMat.SetFloat("_CullMode", (float)UnityEngine.Rendering.CullMode.Off);
        DefaultMat.color = Color.red;
        return DefaultMat;
    }

    void Log(string msg)
    {
        Debug.Log(msg);
    }
}

do premade entities show? eg if you put a cube in a subscene etc does that show?

Yes they show. In this code I get a prefab and it works fine once instantiated. It is altering the material and mesh that I cannot do.

RenderMesh itself is no longer used at runtime.

Refer to this page for info on setup. Make sure values of components are correct after your changes, e.g. make sure MaterialMeshInfo points to the correct offsets in the RenderMeshArray, the RenderBounds has the correct bounds for your mesh, etc.

This is good, here is an updated part of the code. However, I am still unsure if this is the right way to set the new mesh and material, as it gives a unity error.

    [BurstCompile]
    void SetupVariables2(Entity SpawnerEntity)
    {
        Setup = true;
        ChunkSpawnerData Spawner = SystemAPI.GetComponent<ChunkSpawnerData>(SpawnerEntity);
        ChunkSize = Spawner.ChunkSize;
        Log(ChunkSize.ToString());
        Entity Sample = EM.Instantiate(Spawner.Test);
        EntityCommandBuffer ECB = new EntityCommandBuffer(Allocator.TempJob);
        Material Mat = BaseMat(Spawner);
        Mesh ObjectMesh = CreatePlaneMesh();
        Entity Chunk = ECB.Instantiate(Spawner.Test);

        MaterialMeshInfo NewRenderInfo = new MaterialMeshInfo()
        {
            Mesh = ObjectMesh.GetInstanceID(),
            Material = Mat.GetInstanceID(),
            
        };

        ECB.SetName(Chunk, "NEWCHUNK");
        ECB.SetComponent(Chunk, NewRenderInfo);
        ECB.Playback(EM);
        ECB.Dispose();
    }

That’s not correctly setting up MaterialMeshInfo. You should be modifying your RenderMeshArray value (or just create one from scratch) and using MaterialMeshInfo.FromRenderMeshArrayIndices (or FromMaterialMeshIndexRange if using a MaterialMeshIndex array in RenderMeshArray). You also haven’t yet set the RenderBounds component.

Ok I think I’m a step closer, however I can’t get the ID’s right.
Unity error when I do this code.

    [BurstCompile]
    void SetupVariables2(Entity SpawnerEntity)
    {
        Setup = true;
        ChunkSpawnerData Spawner = SystemAPI.GetComponent<ChunkSpawnerData>(SpawnerEntity);
        ChunkSize = Spawner.ChunkSize;
        Log(ChunkSize.ToString());
        Entity Sample = EM.Instantiate(Spawner.Test);
        EntityCommandBuffer ECB = new EntityCommandBuffer(Allocator.TempJob);
        Material Mat = BaseMat(Spawner);
        Mesh ObjectMesh = CreatePlaneMesh();
        Entity Chunk = ECB.Instantiate(Spawner.Test);

        RenderMeshArray NewArray = new RenderMeshArray()
        {
            Meshes = new Mesh[] { ObjectMesh },
            Materials = new Material[] { Mat }
        };
        int MeshID = NewArray.Meshes[0].GetInstanceID();
        int MatID = NewArray.Materials[0].GetInstanceID();
        MaterialMeshInfo NewMM = MaterialMeshInfo.FromRenderMeshArrayIndices(MeshID, MatID, 0);

        ECB.SetName(Chunk, "NEWCHUNK");
        ECB.SetComponent(Chunk, NewMM);
        ECB.Playback(EM);
        ECB.Dispose();
    }

Instance IDs don’t come into this at all, I don’t see where that came from. The indices for MaterialMeshInfo.FromRenderMeshArrayIndices are the indices in the mesh and material arrays you have in your RenderMeshArray.

How does it know which rendermesharray I am refrencing?

The MaterialMeshInfo component on any given entity will be associated with the RenderMeshArray shared component that is also on that entity. Both need to be added/set on the entity.

Ok, still not working, no error but the object doesnt change mesh or material:

    [BurstCompile]
    void SetupVariables2(Entity SpawnerEntity)
    {
        Setup = true;
        ChunkSpawnerData Spawner = SystemAPI.GetComponent<ChunkSpawnerData>(SpawnerEntity);
        ChunkSize = Spawner.ChunkSize;
        Log(ChunkSize.ToString());
        Entity Sample = EM.Instantiate(Spawner.Test);
        EntityCommandBuffer ECB = new EntityCommandBuffer(Allocator.TempJob);
        Material Mat = BaseMat(Spawner);
        Mesh ObjectMesh = CreatePlaneMesh();
        Entity Chunk = ECB.Instantiate(Spawner.Test);

        RenderMeshArray NewArray = new RenderMeshArray()
        {
            Meshes = new Mesh[] { ObjectMesh },
            Materials = new Material[] { Mat }
        };
        int MeshID = NewArray.Meshes[0].GetInstanceID();
        int MatID = NewArray.Materials[0].GetInstanceID();
        MaterialMeshInfo NewMM = MaterialMeshInfo.FromRenderMeshArrayIndices(0, 0, 0);

        ECB.SetName(Chunk, "NEWCHUNK");
        ECB.SetSharedComponentManaged(Chunk, NewArray);
        ECB.SetComponent(Chunk, NewMM);
        ECB.Playback(EM);
        ECB.Dispose();
    }

Is it working with managed Material and Mesh under Burst Compile?

Not sure what you mean. The functions for the material and mesh worked fine on a monobehaviour without dots, so it cant be those functions.

EDIT: If you are asking if i am using burst on the script i have shared, yes everything in the systembase is burst compiled.

It is not. Outside special cases like ISystem’s callback methods like OnCreate/OnUpdate and jobs, you can’t directly Burst-compile instance methods. None of the instance methods in your SystemBase are being Burst-compiled. You can verify this by looking in the Burst Inspector. You can’t use managed types like Mesh and Material inside Burst code anyway.

Perhaps you’re confusing these two instantiations, you’re only modifying one.

Make sure RenderBounds is updated to the correct bounds for the entity with the new mesh/material.

After ensuring the above, what is the exact result you get? Do you see the previous mesh from the prefab, or nothing at all? Can you show screenshots of what’s happening along with the component values for the created entity?

One more note:

Using the array property setters clears the internally stored hash. You will probably see this in the Inspector view for the entity:

image

If you see this (null arrays), either switch to using one of RenderMeshArray’s constructors that will compute the hash at the end by itself, or use ResetHash128 after setting the arrays.

1 Like

How do i check the rendermesh array property like in that image?

Select the entity in the Entities Hierarchy view. GameObjects are wireframe boxes while entities are outlines of boxes. You can see entities in loaded subscenes by using Runtime data mode, or by closing the subscene for editing (checkmark on the Subscene entry in Hierarchy view or in Entities Hierarchy) when in mixed/authoring modes. Runtime-instantiated (from prefabs in a subscene) entities require closing the susbcene.

As in left-clicking on entities during runtime through the hiearchy? My inspector is blank when selected