Search Unity

Resolved renderMeshArray.GetMesh(materialMeshInfo) returns null, why?

Discussion in 'Graphics for ECS' started by andrew-lukasik, Nov 10, 2022.

  1. andrew-lukasik

    andrew-lukasik

    Joined:
    Jan 31, 2013
    Posts:
    249
    renderMeshArray.GetMesh( materialMeshInfo ) returns null, why?
    Code (CSharp):
    1. using UnityEngine;
    2. using Unity.Entities;
    3. using Unity.Rendering;
    4.  
    5. public partial class MeshCollectorSystem : SystemBase
    6. {
    7.     protected override void OnUpdate ()
    8.     {
    9.         foreach( var (renderMeshArray,materialMeshInfo,entity) in SystemAPI.Query<RenderMeshArray,MaterialMeshInfo>().WithEntityAccess() )
    10.         {
    11.             Mesh mesh = renderMeshArray.GetMesh( materialMeshInfo );
    12.             if( mesh==null ) throw new System.NullReferenceException();
    13.         }
    14.     }
    15. }
    Here is a temporary walkaround I found:
    Code (CSharp):
    1. Mesh mesh = renderMeshArray.Meshes[ materialMeshInfo.Mesh-1 ];
    2. // it is likely totally wrong but works in my case ¯\_(ツ)_/¯
    so source meshes are not null
     
    Last edited: Nov 10, 2022
  2. JussiKnuuttila

    JussiKnuuttila

    Unity Technologies

    Joined:
    Jun 7, 2019
    Posts:
    351
    In the current release, MaterialMeshInfo is rewritten to always contain a runtime ID (the one returned by RegisterMesh). What happens is that on each frame, there is a job that runs which looks at each entity that has a changed MaterialMeshInfo, and replaces array indices in them with real mesh IDs. This mesh ID is not a valid array index, which is why this code doesn't work.

    The intent of this is to make it possible to render the entity very fast, without requiring any extra data to be loaded except the MaterialMeshInfo component.

    This behavior is admittedly rather confusing, and is known to cause other issues as well, so we are aiming to remove this behavior in the next release, at which point your code should also work.
     
    StefanWo and andrew-lukasik like this.
  3. andrew-lukasik

    andrew-lukasik

    Joined:
    Jan 31, 2013
    Posts:
    249
    Thank you for your explanation. Is there a different way I can find which mesh matches given entity data right now?


    Ok. I should have looked into the source code earlier, null is the expected behavior indeed
    Code (CSharp):
    1. public Mesh GetMesh(MaterialMeshInfo materialMeshInfo)
    2. {
    3.     if (materialMeshInfo.IsRuntimeMesh)
    4.         return null;
    5.     else
    6.         return Meshes[materialMeshInfo.MeshArrayIndex];
    7. }
     
    Last edited: Nov 10, 2022
  4. JussiKnuuttila

    JussiKnuuttila

    Unity Technologies

    Joined:
    Jun 7, 2019
    Posts:
    351
    I'm afraid there isn't a great way at the moment. The easiest option is to use EntitiesGraphicsSystem.GetMesh(), but that API is not currently public, so you would have to modify the package source and make it public to use it.

    With public APIs, I believe the best way would be to add a custom Baker to track this at Baking time already (where you can access the source MeshRenderer), or do the equivalent from your generation code if you are generating these entities at runtime.
     
    andrew-lukasik likes this.
  5. andrew-lukasik

    andrew-lukasik

    Joined:
    Jan 31, 2013
    Posts:
    249
    Thank you for your advice! Both solutions have their advantages.

    Accessing EntitiesGraphicsSystem.GetMesh() with reflection is good enough for my case for now
    Code (CSharp):
    1. var renderer = World.GetExistingSystemManaged<EntitiesGraphicsSystem>();
    2. Mesh mesh = (Mesh) typeof(EntitiesGraphicsSystem)
    3.     .GetMethod( "GetMesh" , BindingFlags.Instance | BindingFlags.NonPublic )
    4.     .Invoke( renderer , new object[]{ materialMeshInfo.MeshID } );
     
    Last edited: Nov 11, 2022