Search Unity

Question Runtime mesh changes resulting in nothing rendered?

Discussion in 'Graphics for ECS' started by CynicalBusiness, Mar 7, 2023.

  1. CynicalBusiness

    CynicalBusiness

    Joined:
    May 3, 2020
    Posts:
    10
    I am trying to update the mesh on a large number of entities at runtime, based on data in the entity's other components (for some dynamic terrain). However, no matter what I try, I cannot seem to get the EntitiesGraphicsSystem to actually render the meshes, resulting in a completely empty scene.

    Distilled down, my mesh building code looks like this:
    Code (CSharp):
    1.  
    2. private void UpdateDirtyMeshes()
    3. {
    4.     using var dirtyEntities = tileWithDirtyGroundQuery.ToEntityArray(Allocator.Temp);
    5.  
    6.     if (dirtyEntities.Length == 0)
    7.     {
    8.         // skip updating if nothing needs it
    9.         return;
    10.     }
    11.  
    12.     // make some mesh data arrays for the job to build the meshes
    13.     var meshDataArrays = new UnsafeList<Mesh.MeshDataArray>(dirtyEntities.Length, Allocator.TempJob);
    14.     for (var i = 0; i < dirtyEntities.Length; i++)
    15.     {
    16.         meshDataArrays.AddNoResize(Mesh.AllocateWritableMeshData(1));
    17.     }
    18.  
    19.     // pass the data arrays to the job to populate
    20.     var meshBuildJob = new MeshBuildJob
    21.     {
    22.         // other stuff...
    23.         MeshDataArrays = meshDataArrays
    24.     };
    25.     var meshBuildJobHandle = meshBuildJob.Schedule(dirtyEntities.Length, 1, Dependency);
    26.     meshBuildJobHandle.Complete();
    27.  
    28.     for (var i = 0; i < dirtyEntities.Length; i++)
    29.     {
    30.         // get the "ground" entity which holds the mesh
    31.         var ground = EntityManager.GetComponentData<MapTileGroundEntity>(dirtyEntities[i]).Value;
    32.         var mmi = EntityManager.GetComponentData<MaterialMeshInfo>(ground);
    33.  
    34.         // in this case, I'm dropping the old mesh and making a new one
    35.         // I have also tried to simply update the existing mesh, but that doesn't work either
    36.         if (mmi.Mesh > 0)
    37.         {
    38.             // unregister the old mesh
    39.             entitiesGraphicsSystem.UnregisterMesh(new BatchMeshID { value = (uint)mmi.Mesh });
    40.         }
    41.  
    42.         // build and register a new mesh
    43.         var mesh = new Mesh() { name = "GroundMesh_" + ground.Index };
    44.         Mesh.ApplyAndDisposeWritableMeshData(meshDataArrays[i], mesh);
    45.         mesh.RecalculateNormals();
    46.         mesh.RecalculateBounds();
    47.         mesh.RecalculateTangents();
    48.  
    49.         // for debugging, update a component that shows us the mesh in the inspector
    50.         EntityManager.SetComponentData(ground, new MapTileGroundMesh { Value = mesh });
    51.  
    52.         // update the bounds on the component
    53.         var bounds = mesh.bounds;
    54.         EntityManager.SetComponentData(entity, new RenderBounds { Value = new() { Center = bounds.center, Extents = bounds.extents } });
    55.  
    56.         // register the new mesh with the EGS and update the mesh ID
    57.         var meshId = entitiesGraphicsSystem.RegisterMesh(mesh);
    58.         EntityManager.SetComponentData(ground, new MaterialMeshInfo { Mesh = (int)meshId.value, Material = mmi.Material, Submesh = mmi.Submesh });
    59.     }
    60.  
    61.     // additional work on other parts of the entity, cleanup/disposing things, etc.
    62.     // ...
    63. }
    64.  

    For reference, this entity is built as a prototype and cloned earlier by EntityManager.Instantiate then adding a Parent component to it once built.

    The prototype is built like so:
    Code (CSharp):
    1.  
    2. EntityManager entityManager = // ...
    3. Material material = // This is currently using the standard URP "Lit" material
    4.  
    5. entityManager.AddComponentData(entity, new LocalTransform { Scale = 1 });
    6. entityManager.AddComponent<WorldTransform>(entity);
    7. entityManager.AddComponent<LocalToWorld>(entity);
    8. RenderMeshUtility.AddComponents
    9.     entityManager,
    10.     entity,
    11.     new RenderMeshDescription(ShadowCastingMode.ShadowsOnly),
    12.     new RenderMeshArray(new Material[] { material }, new Mesh[0]),
    13.     new MaterialMeshInfo { Mesh = 0, Material = -1 });
    14. // plus my custom components, too
    15.  

    From my understanding by reading through the scripts, MaterialMeshInfo's Mesh/Material properties can be positive for a batched mesh's ID, or negative to index the shared RenderMeshArray. Since they all share a material, it can be in the RenderMeshArray, but I'll be updating the mesh at runtime and each will have a different mesh (plus don't want structural changes for doing so), so that's just going to be zero for now.

    After this method executes, I can see all my tiles in the hierarchy window correctly:
    upload_2023-3-7_15-59-34.png
    upload_2023-3-7_15-59-19.png
    (The appropriate WorldRenderBounds is also correct, per the tile's transform)

    I can also inspect the resulting mesh to find it looks correct when viewed in the inspector's little viewport. So, all that appears to have worked:
    upload_2023-3-7_15-55-25.png
    (It's a very simple mesh for now, but will get more complicated once this is working)

    Yet, despite that, nothing is rendered in the scene: it is in fact completely empty. I've been at this for quite some time and just cannot figure out what the missing piece is; what am I doing wrong?

    Unity Editor 2022.2.7f1
    Universal Rendering Pipeline 14.0.6
    Unity.Entities 1.0.0-pre.47
    Unity.Entities.Rendering 1.0.0-pre.44
     
  2. Fribur

    Fribur

    Joined:
    Jan 5, 2019
    Posts:
    136
    Looks very similar to what I do and it works. Two ideas: (1) not casting BatchMeshID. (2) check if LocalTransform is set correctly (i.e. that it does not have scale „0“ or invalid rotation)?

    Edit: it think your bug is in how you setup MaterialMeshInfo. I set it up using BatchMaterialID and BatchMeshID.

    Code (CSharp):
    1. BatchMaterialID materialID = hybridRenderer.RegisterMaterial(yourMaterial)
    2. BatchMeshID meshID = hybridRenderer.RegisterMesh(yourMesh)
    3. entityManager.SetComponentData(prototype, new MaterialMeshInfo(materialID, meshID));
    4. or
    5. _entityManager.SetComponentData(prototype, new MaterialMeshInfo {MeshID = meshID, MaterialID = materialID });

    Code (CSharp):
    1. var meshID = hybridRenderer.RegisterMesh(receivingMeshArray[0]);
    2. //I manually add components instead of using RenderMeshUtility function
    3. _entityManager.AddComponent(prototype, renderTypeset);
    4. _entityManager.AddComponent(prototype, areaOrBoxColorTypeset);
    5. _entityManager.AddComponentData(prototype, new AreaColorMeshState { meshID = meshID });//ICleanupComponentData used to track destruction to enable cleanup (unregister mesh, destroy mesh)
    6. _entityManager.SetSharedComponentManaged(prototype, renderMeshDescription.FilterSettings);
    7. _entityManager.SetComponentData(prototype, new MaterialMeshInfo(materialID, meshID));
    8. _entityManager.SetComponentData(prototype, new RenderBounds { Value = receivingMeshArray[0].bounds.ToAABB() });
    9. _entityManager.SetComponentData(prototype, LocalTransform.Identity);//ensures that scale is not zero, rotation is quaternion.identity
    10.  
     
    Last edited: Mar 8, 2023
  3. JussiKnuuttila

    JussiKnuuttila

    Unity Technologies

    Joined:
    Jun 7, 2019
    Posts:
    351
    You seem to be specifying "ShadowsOnly", which means that the entities will only be rendered into shadow maps, and not the main output. Have you tried using ShadowCastingMode.On instead?

    If that does not help, I would suggest trying Unity's Frame Debugger or RenderDoc to see whether there is any rendering taking place. If there are draw calls being made but nothing is rendering, that could suggest e.g. something wrong with the transforms. If there are no draw calls, then it suggests a remaining problem in the configuration of the entities.