renderMeshArray.GetMesh( materialMeshInfo ) returns null, why?
using UnityEngine;
using Unity.Entities;
using Unity.Rendering;
public partial class MeshCollectorSystem : SystemBase
{
protected override void OnUpdate ()
{
foreach( var (renderMeshArray,materialMeshInfo,entity) in SystemAPI.Query<RenderMeshArray,MaterialMeshInfo>().WithEntityAccess() )
{
Mesh mesh = renderMeshArray.GetMesh( materialMeshInfo );
if( mesh==null ) throw new System.NullReferenceException();
}
}
}
Here is a temporary walkaround I found:
Mesh mesh = renderMeshArray.Meshes[ materialMeshInfo.Mesh-1 ];
// it is likely totally wrong but works in my case ¯\_(ツ)_/¯
so source meshes are not null
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.
2 Likes
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
GetMesh(MaterialMeshInfo materialMeshInfo)
public Mesh GetMesh(MaterialMeshInfo materialMeshInfo)
{
if (materialMeshInfo.IsRuntimeMesh)
return null;
else
return Meshes[materialMeshInfo.MeshArrayIndex];
}
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.
1 Like
Thank you for your advice! Both solutions have their advantages.
Accessing EntitiesGraphicsSystem.GetMesh() with reflection is good enough for my case for now
var renderer = World.GetExistingSystemManaged<EntitiesGraphicsSystem>();
Mesh mesh = (Mesh) typeof(EntitiesGraphicsSystem)
.GetMethod( "GetMesh" , BindingFlags.Instance | BindingFlags.NonPublic )
.Invoke( renderer , new object[]{ materialMeshInfo.MeshID } );