ECS: multiple materials? - SOLVED.

I have some trees that have a single mesh and multiple materials (bark and leaves) I am attempting to line my streets with trees in ECS, but I can’t figure out how to apply a second material.

On Reddit someone mentioned the subMesh value, but this is just an int. If I set that to 1 and then apply the second material, ONLY the second material is shown. So obviously I am doing something wrong

int randomValue = UnityEngine.Random.Range(0, treesComponentList.Count);
Mesh propMesh = treesComponentList[randomValue].mesh;
Material propMaterial = treesComponentList[randomValue].materials[0];

RenderMesh renderMesh = new RenderMesh();
renderMesh.material = propMaterial;
renderMesh.mesh = propMesh;

renderMesh.subMesh = 1;
renderMesh.material = treesComponentList[randomValue].materials[1];

You’ll need two RenderMesh instances, one for each submesh and material.

2 Likes

Ok, sounds logical. So specifically how is this achieved? My current archetype is:
EntityArchetype entityArchetype = entityManager.CreateArchetype(
typeof(SidewalkTreeTag),
typeof(Translation),
typeof(RenderMesh),
typeof(Scale),
typeof(LocalToWorld)
);

//I attempted to get this working like this:
Entity entity = entityManager.CreateEntity(entityArchetype);
entityManager.SetComponentData(entity, new Translation { Value = position });
entityManager.SetComponentData(entity, new Scale { Value = treeScale });
entityManager.SetSharedComponentData(entity, new RenderMesh { mesh = propMesh, material = propMaterial, subMesh = 0 });

if (treesComponentList[0].materials.Count > 1)
{
entityManager.AddComponent(entity, typeof(RenderMesh));
entityManager.SetSharedComponentData(entity,
new RenderMesh {
mesh = propMesh,
material = treesComponentList[randomValue].materials[1],
subMesh = 0 });
}

I tried setting the subMesh to 0 and to 1, in each case I still end up with just the Bark showing on the tree (the bark is the first material in the list. So, it seems that I am setting the first RenderMesh, and not the second… or, well, I just don’t know. Care to enlighten me? Slap me and say; “hey stupid, it’s like this:” I’m ok with that.

Use two children entities each with a RenderMesh. You’ll probably want to instantiate from a prefab rather than an archetype for this.

You should create another entity and set RM. You can’t have many IComponentData\ISharedComponentData on the same entity.
Edit: Ah @DreamingImLatios already answered, I’m not updated page :slight_smile:

Ok, I’m going to have to be annoying. (SO sorry) But is there an example of this?

It sounds like I should create one entity, and that holds what, just two child entities? I would think you are saying I add two child entities to that entity and add the RenderMesh component to each of the child components, right?

I always work best when I can see an example, and I will be giving the above idea a try right. I am attempting to do the above idea, but I’m just not getting there. Feel free to point and laugh.

No, you need only two entities for this case. Just look at how conversion workflow works, look at unity samples github and inspect which components parent and child entities have. Spoiler - Parent component data, Child buffer, LocalToParent component data etc.

2 Likes

So correct me if I am wrong (PLEASE!) I don’t need a parent, but each material is going to need a new entity. I just got the code working like this:

for (int p = 0; p < treesComponentList[randomValue].materials.Count; p++)
{
Entity entity = entityManager.CreateEntity(entityArchetype);
entityManager.SetComponentData(entity, new Translation { Value = position });
entityManager.SetComponentData(entity, new Scale { Value = treeScale });
entityManager.SetSharedComponentData(
entity,
 new RenderMesh {
 mesh = propMesh,
  material = treesComponentList[randomValue].materials[p],
  subMesh = p });
}

because of this I have two entities for one tree, is this correct?

If it looks like you want, why not? :slight_smile: If your threes shouldn’t be in hierarchy (for example they wouldn’t move like some solid thing) it’s fine without parenting

1 Like

Thank you all for the helpful info.

In hopes this helps the next person, here is my code for taking a list of IComponentData, shown in the inspector, to draw at startup

using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public struct MeshMaterialsComponents
{
    public Mesh mesh;
    public List<Material> materials;
}

which is used like this

[Header("Sidewalk Trees")]
    [SerializeField]
    private List<MeshMaterialsComponents> treesComponentList = new List<MeshMaterialsComponents>();

Then I have a function that loads just trees (I’m using Easy Roads 3D, a great asset with lots of data available via it’s api) With that I look through all roads, get their data points and loop through those to add trees

float treeScale = UnityEngine.Random.Range(0.8f, 1.2f);
int randomValue = UnityEngine.Random.Range(0, treesComponentList.Count);
Vector3 translation = DistanceBetweenPoints(markersSide[i], markersCenter[i], treeOffset);translation.y += propYOffset;

                    for (int p = 0; p < treesComponentList[randomValue].materials.Count; p++)
                    {
                        Entity entity = entityManager.CreateEntity(entityArchetype);
                        entityManager.SetComponentData(entity, new Translation { Value = translation });
                        entityManager.SetComponentData(entity, new Scale { Value = treeScale });
                        entityManager.SetSharedComponentData(
                            entity,
                            new RenderMesh {
                                mesh = treesComponentList[randomValue].mesh,
                                material = treesComponentList[randomValue].materials[p],
                                subMesh = p });
                    }
4 Likes