Search Unity

Question Adding mesh to Entity

Discussion in 'Graphics for ECS' started by constab, Sep 17, 2023.

  1. constab

    constab

    Joined:
    Dec 20, 2019
    Posts:
    6
    Hello guys!

    I am having trouble to adding Mesh to Entity.
    I have tried different methods, but nothing work.
    I have a system that uses some prefab (without meshFilter and meshRenderer) to create Entity, and I would like to add specific mesh (lfor example square 1x1 or 2x2 or 1x2 and etc.).
    I tried google it, but did not find nothing...
    I tried following:

    Code (CSharp):
    1. var ecb = new EntityCommandBuffer(Allocator.Temp);
    2.  
    3. ...
    4.  
    5. var mesh = new Mesh();
    6. mesh.vertices = new[] { Vector3.up * size, Vector3.left * size, Vector3.right * size };
    7. mesh.triangles = new[] { 0, 1, 2, 0, 2, 1 };
    8.  
    9. ecb.SetComponent(newItem, new RenderMesh
    10. {
    11.     mesh = mesh,
    12.     material = material,
    13. });
    or

    Code (CSharp):
    1.         [BurstCompile]
    2.         public void OnUpdate(ref SystemState state)
    3.         {
    4.             var worldEntity = SystemAPI.GetSingletonEntity<WorldProperties>();
    5.             var world = SystemAPI.GetAspect<WorldAspect>(worldEntity);
    6.             var ecb = new EntityCommandBuffer(Allocator.Temp);
    7.             var newMachine = ecb.Instantiate(world.MachinePrefab);
    8.  
    9.             var desc = new RenderMeshDescription(
    10.                 shadowCastingMode: ShadowCastingMode.Off,
    11.                 receiveShadows: false);
    12.  
    13.             var material = new Material(Shader.Find("Universal Render Pipeline/Lit"));
    14.  
    15.             var mesh = new Mesh();
    16.             mesh.vertices = new[] { Vector3.up * 2, Vector3.left * 2, Vector3.right * 2 };
    17.             mesh.triangles = new[] { 0, 1, 2, 0, 2, 1 };
    18.  
    19.             var renderMeshArray = new RenderMeshArray(
    20.                 new Material[]
    21.                 {
    22.                     material,
    23.                 },
    24.                 new Mesh[]
    25.                 {
    26.                     mesh
    27.                 }
    28.             );
    29.  
    30.             RenderMeshUtility.AddComponents(
    31.                 newMachine,
    32.                 World.DefaultGameObjectInjectionWorld.EntityManager,
    33.                 desc,
    34.                 renderMeshArray,
    35.                 MaterialMeshInfo.FromRenderMeshArrayIndices(0, 0)
    36.             );
    What the correct way?

    Thanks,
    Andrei.
     
  2. JussiKnuuttila

    JussiKnuuttila

    Unity Technologies

    Joined:
    Jun 7, 2019
    Posts:
    351
    The latter version looks mostly correct, but if you want to use an EntityCommandBuffer for the Instantiate, then you should Playback it before calling RenderMeshUtility.AddComponents. In this kind of use case it would probably be easier to just use World.DefaultGameObjectInjectionWorld.EntityManager.Instantiate instead.
     
  3. constab

    constab

    Joined:
    Dec 20, 2019
    Posts:
    6
    Hi Jussi,

    You're a wizard!
    Your solution to use World.DefaultGameObjectInjectionWorld.EntityManager.Instantiate worked, and finally I saw my damn triangle :)
    Working version is following:

    Code (CSharp):
    1.         public void OnUpdate(ref SystemState state)
    2.         {
    3.             state.Enabled = false;
    4.  
    5.             var worldEntity = SystemAPI.GetSingletonEntity<WorldProperties>();
    6.             var world = SystemAPI.GetAspect<WorldAspect>(worldEntity);
    7.             var entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
    8.             var newMachine = entityManager.Instantiate(world.MachinePrefab);
    9.             var desc = new RenderMeshDescription(
    10.                 shadowCastingMode: ShadowCastingMode.Off,
    11.                 receiveShadows: false);
    12.             var material = new Material(Shader.Find("Universal Render Pipeline/Lit"));
    13.             var mesh = new Mesh();
    14.             mesh.vertices = new[] { Vector3.up * 2, Vector3.left * 2, Vector3.right * 2 };
    15.             mesh.triangles = new[] { 0, 1, 2, 0, 2, 1 };
    16.             var renderMeshArray = new RenderMeshArray(
    17.                 new Material[] { material },
    18.                 new Mesh[] { mesh });
    19.  
    20.             RenderMeshUtility.AddComponents(
    21.                 newMachine,
    22.                 entityManager,
    23.                 desc,
    24.                 renderMeshArray,
    25.                 MaterialMeshInfo.FromRenderMeshArrayIndices(0, 0)
    26.             );
    27.  
    28.             entityManager.AddComponentData(newMachine, new LocalToWorld());
    29.         }
    But now I have a few new questions:
    1. I'm encountering Burst error:
    ./Library/PackageCache/com.unity.entities@1.0.14/Unity.Entities/EntityDataAccess.cs(2113,13): Burst error BC1051: Invalid managed type found for the field `m_Materials` of the struct `Unity.Rendering.RenderMeshArray`.: the type `UnityEngine.Material[]` is a managed type and is not supported
    This error on the line with RenderMeshUtility.AddComponents. So I removed [BurstCompile] for OnUpdate(ref SystemState state) function in my System. But how can I resolve this issue that Burst start works?
    2. How I can use EntityCommandBuffer? I tried following:
    Code (CSharp):
    1. ...
    2.             var entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
    3.            
    4.             var ecb = new EntityCommandBuffer(Allocator.Temp);
    5.             var newMachine = ecb.Instantiate(world.MachinePrefab);
    6.             ecb.SetComponent(newMachine, new LocalTransform
    7.             {
    8.                 Position = new float3 { x = 1.0f, y = 2.0f, z = 0.0f },
    9.                 Rotation = quaternion.identity,
    10.                 Scale = 1.0f,
    11.             });
    12.  
    13.             ecb.Playback(entityManager);
    14.             ecb.Dispose();
    15.  
    16.             var desc = new RenderMeshDescription(
    17.                 shadowCastingMode: ShadowCastingMode.Off,
    18.                 receiveShadows: false);
    19. ...
    But this approach doesn't work for me. I received error:
    ArgumentException: All entities created using EntityCommandBuffer.CreateEntity must be realized via playback(). One of the entities is still deferred (Index: -1).

    Please forgive me for my stupid questions. I'm a newbie in DOTS...
     
  4. JussiKnuuttila

    JussiKnuuttila

    Unity Technologies

    Joined:
    Jun 7, 2019
    Posts:
    351
    The RenderMeshArray type is not Burst compatible, because it contains Mesh and Material objects, which are managed class objects.

    The whole reason why we introduced RenderMeshArray was so that code can be split into Burst compatible parts that can use integers (for example, the MaterialMeshInfo component), and Burst incompatible parts that have to deal with RenderMeshArray directly, but which usually are run infrequently.

    Unfortunately, I'm not sure why the EntityCommandBuffer code does not work, so I'm not able to help you there.
     
  5. constab

    constab

    Joined:
    Dec 20, 2019
    Posts:
    6
    Jussi, anyway, thanks for the help!

    I still don't know how it will be implemented :)
    For now, I see, that I will have system that will create entities. For example, if I need to create 2x2 house, then the system will create new entity with 2x2 mesh, will set some specific material, texture for scheder, and set component with health, resistances and etc...
    But really I'm not sure that this good apprach.
    I don't know how mach types of building I'll have. I can assume that there will be up to hundred of them, and the majority will be 1x1 or 2x2.
    Maybe should I create separate prefab for each size? Or create one prefab that will contains all meshes?
    Really I have not experience with it :(