Search Unity

ECS: multiple materials? - SOLVED.

Discussion in 'Entity Component System' started by JamesWjRose, Oct 16, 2019.

  1. JamesWjRose

    JamesWjRose

    Joined:
    Apr 13, 2017
    Posts:
    687
    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];
     
  2. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,269
    You'll need two RenderMesh instances, one for each submesh and material.
     
    Chmyke and JamesWjRose like this.
  3. JamesWjRose

    JamesWjRose

    Joined:
    Apr 13, 2017
    Posts:
    687
    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.
     
  4. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,269
    Use two children entities each with a RenderMesh. You'll probably want to instantiate from a prefab rather than an archetype for this.
     
  5. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    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 :)
     
  6. JamesWjRose

    JamesWjRose

    Joined:
    Apr 13, 2017
    Posts:
    687
    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.
     
  7. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    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.
     
    Chmyke and JamesWjRose like this.
  8. JamesWjRose

    JamesWjRose

    Joined:
    Apr 13, 2017
    Posts:
    687
    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:

    Code (CSharp):
    1.  
    2.  
    3. for (int p = 0; p < treesComponentList[randomValue].materials.Count; p++)
    4. {
    5. Entity entity = entityManager.CreateEntity(entityArchetype);
    6. entityManager.SetComponentData(entity, new Translation { Value = position });
    7. entityManager.SetComponentData(entity, new Scale { Value = treeScale });
    8. entityManager.SetSharedComponentData(
    9. entity,
    10.  new RenderMesh {
    11.  mesh = propMesh,
    12.   material = treesComponentList[randomValue].materials[p],
    13.   subMesh = p });
    14. }
    because of this I have two entities for one tree, is this correct?
     
  9. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    If it looks like you want, why not? :) If your threes shouldn’t be in hierarchy (for example they wouldn’t move like some solid thing) it’s fine without parenting
     
    JamesWjRose likes this.
  10. JamesWjRose

    JamesWjRose

    Joined:
    Apr 13, 2017
    Posts:
    687
    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

    Code (CSharp):
    1.  
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. [System.Serializable]
    6. public struct MeshMaterialsComponents
    7. {
    8.     public Mesh mesh;
    9.     public List<Material> materials;
    10. }
    which is used like this
    Code (CSharp):
    1.    
    2. [Header("Sidewalk Trees")]
    3.     [SerializeField]
    4.     private List<MeshMaterialsComponents> treesComponentList = new List<MeshMaterialsComponents>();
    5.  
    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

    Code (CSharp):
    1.                    
    2. float treeScale = UnityEngine.Random.Range(0.8f, 1.2f);
    3. int randomValue = UnityEngine.Random.Range(0, treesComponentList.Count);
    4. Vector3 translation = DistanceBetweenPoints(markersSide[i], markersCenter[i], treeOffset);translation.y += propYOffset;
    5.  
    6.                     for (int p = 0; p < treesComponentList[randomValue].materials.Count; p++)
    7.                     {
    8.                         Entity entity = entityManager.CreateEntity(entityArchetype);
    9.                         entityManager.SetComponentData(entity, new Translation { Value = translation });
    10.                         entityManager.SetComponentData(entity, new Scale { Value = treeScale });
    11.                         entityManager.SetSharedComponentData(
    12.                             entity,
    13.                             new RenderMesh {
    14.                                 mesh = treesComponentList[randomValue].mesh,
    15.                                 material = treesComponentList[randomValue].materials[p],
    16.                                 subMesh = p });
    17.                     }
    18.