Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Resolved How to query a RenderMeshArray?

Discussion in 'Graphics for ECS' started by dherault, Apr 16, 2023.

  1. dherault

    dherault

    Joined:
    Dec 19, 2019
    Posts:
    8
    Hi,

    I'm using Unity 2022 URP and ECS 1.0.0-pre.65 with the corresponding Entities Graphics package.

    In a ISystem I would like to query a RenderMeshArray

    Using
    Code (CSharp):
    1. foreach (
    2.     var (gameState, renderMeshArray)
    3.     in SystemAPI.Query<RefRO<GameState>, RefRW<RenderMeshArray>>()
    4. )
    Gives the following error:
    `The type 'Unity.Rendering.RenderMeshArray' must be convertible to 'Unity.Entities.IComponentData' in order to use it as parameter 'T' in the generic struct 'Unity.Entities.RefRW<T>'`

    Where should I start?
    Note that my goal is to create a system that modifies the Mesh of its entities at each update or so.
     
  2. JussiKnuuttila

    JussiKnuuttila

    Unity Technologies

    Joined:
    Jun 7, 2019
    Posts:
    351
    If you want to set the Mesh to a different Mesh (and not modify the Mesh object itself), I would recommend using MaterialMeshInfo, which is how you set the Mesh that each Entity uses for rendering.
     
  3. dherault

    dherault

    Joined:
    Dec 19, 2019
    Posts:
    8
    Yes, that's a good point. But I'd like to modify the mesh according to some algorithm. So I need access to the vertices/triangles/uvs in a System.
     
  4. davidus2010

    davidus2010

    Joined:
    Dec 4, 2017
    Posts:
    72
    For your purpose you likely still need to use the MaterialMeshInfo component. Check out the code for it, it contains ids for the material and mesh of the entity, which point to either materials and meshes in the entity's rendermesharray, or to meshes registered directly to the BatchedRendererGroup.
    If your meshes are stored in the RenderMeshArray, there's a method on it to get the correct mesh based on the MaterialMeshInfo:
    Code (CSharp):
    1.  /// <summary>
    2.         /// Gets the mesh for given MaterialMeshInfo.
    3.         /// </summary>
    4.         /// <param name="materialMeshInfo">The MaterialMeshInfo to use.</param>
    5.         /// <returns>Returns the associated Mesh instance or null if the mesh is runtime.</returns>
    6.         public Mesh GetMesh(MaterialMeshInfo materialMeshInfo)
    Note that the RenderMeshArray is a shared, managed component. so you can't access it in the exact same way as normal components.
     
  5. dherault

    dherault

    Joined:
    Dec 19, 2019
    Posts:
    8
    So, I got it working, here is my take on this. I have a baked component called Meshable to identify the prefabs with the mesh I want to modify.

    In an ISystem, in OnStartRunning:
    Code (CSharp):
    1.  
    2. var meshableEntities = state.GetEntityQuery(typeof(Meshable)).ToEntityArray(AllocatorManager.TempJob);
    3. var firstMeshableEntity = meshableEntities[0];
    4. // Of course in the current ECS implementation, the RenderMeshArray is on a child entity
    5. var linkedEntityGroup = state.EntityManager.GetBuffer<LinkedEntityGroup>(firstMeshableEntity);
    6. var childEntity = linkedEntityGroup[1].Value;
    7. var renderMeshArray = state.EntityManager.GetSharedComponentManaged<RenderMeshArray>(childEntity);
    8.  
    Then in the same function I operate on the RenderMeshArray. Here I'm creating 128 meshes:
    Code (CSharp):
    1.  
    2. var n = 128;
    3. var meshes = new Mesh[n];
    4.  
    5. for (var i = 0; i < n; i++)
    6. {
    7.     meshes[i] = new Mesh();
    8. }
    9.  
    10. renderMeshArray.Meshes = meshes;
    11. renderMeshArray.ResetHash128();
    12.  
    Still in OnStartRunning, I can now assign the newly created meshes to my entities:

    Code (CSharp):
    1.  
    2. var index = 0;
    3.            
    4. foreach (var meshableEntity in meshableEntities)
    5. {
    6.     var meshableLinkedEntityGroup = state.EntityManager.GetBuffer<LinkedEntityGroup>(meshableEntity);
    7.     // Retrieve the two linked child entities that ECS v1 create to render the prefab
    8.     var meshableChildEntity1 = meshableLinkedEntityGroup[1].Value;
    9.     var meshableChildEntity2 = meshableLinkedEntityGroup[2].Value;
    10.     var materialMeshInfo1 = state.EntityManager.GetComponentData<MaterialMeshInfo>(meshableChildEntity1);
    11.     var materialMeshInfo2 = state.EntityManager.GetComponentData<MaterialMeshInfo>(meshableChildEntity2);
    12.     // Assign a unique mesh from the RenderMeshArray to each entity
    13.     var staticIndex = MaterialMeshInfo.ArrayIndexToStaticIndex(index);
    14.    
    15.     materialMeshInfo1.Mesh = staticIndex;
    16.     materialMeshInfo2.Mesh = staticIndex;
    17.     // Mark as meshable child for easy query in OnUpdate
    18.     state.EntityManager.AddComponent<MeshableChild>(meshableChildEntity1);
    19.     state.EntityManager.AddComponent<MeshableChild>(meshableChildEntity2);
    20.     state.EntityManager.SetComponentData(meshableChildEntity1, materialMeshInfo1);
    21.     state.EntityManager.SetComponentData(meshableChildEntity2, materialMeshInfo2);
    22.     state.EntityManager.SetSharedComponentManaged(meshableChildEntity1, renderMeshArray);
    23.     state.EntityManager.SetSharedComponentManaged(meshableChildEntity2, renderMeshArray);
    24.  
    25.     // Omited: SetComponentData for renderBounds and worldRenderBounds
    26.     index++;
    27. }
    28.  
    Then in OnUpdate:
    Code (CSharp):
    1.  
    2. var defaultRenderMeshArray = default(RenderMeshArray);
    3. var renderMeshArray = defaultRenderMeshArray;
    4.  
    5. foreach (
    6.     var (meshableChild, materialMeshInfo, entity)
    7.     in SystemAPI.Query<RefRO<MeshableChild>, RefRO<MaterialMeshInfo>>().WithEntityAccess()
    8. )
    9. {
    10.     if (renderMeshArray == defaultRenderMeshArray)
    11.     {
    12.         renderMeshArray = state.EntityManager.GetSharedComponentManaged<RenderMeshArray>(entity);
    13.     }
    14.  
    15.     var mesh = renderMeshArray.GetMesh(materialMeshInfo.ValueRO);
    16.  
    17.     DoSomethingWithMesh(mesh);
    18. }
    19.  
    And there you go: a way to modify meshes in a system.
    Hope this helps, if you find a better approach please share, thanks.
     
  6. IAmChiagozie

    IAmChiagozie

    Joined:
    Jun 26, 2017
    Posts:
    31
    Any idea on how to set the material on an entity?
     
  7. steeldynamite

    steeldynamite

    Joined:
    Jun 15, 2020
    Posts:
    4
    There is a better approach and a solution for how to set materials on an entity:

    EntitiesGraphicsSystem has methods for `GetMesh`, `RegisterMesh`, `UnregisterMesh` as well as `GetMaterial`, `RegisterMaterial`, and `UnregisterMaterial`.

    In your system, you'll want to get a reference to `state.World.GetExistingSystemManaged<EntitiesGraphicsSystem>()` and register your materials/meshes to get a BatchMaterialID/BatchMeshID which is just a wrapper for the integer index that you can assign in MaterialMeshInfo.

    Negative indices in MaterialMeshInfo make it use RenderMeshArray while positive indices are registered in the graphics system. When you register a material/mesh it will increment an owner counter, unregistering will decrement.
     
    andywatts and UniqueCode like this.