Skip to main content
Participant
February 6, 2025
Solved

Creating a mesh programatically

  • February 6, 2025
  • 3 replies
  • 952 views

Given an array of vertices, normals, texture coordinates, and a triangle index list, how do I create a mesh that that I can load into an ECS Entity? 

    This topic has been closed for replies.
    Best answer by davs

    Hi!

    There are a couple of different pieces of this, so I will break them down in two parts.

    Creating a custom SceneMesh

    The SceneMesh is the actual object that represents the geometry that we are rendering. It is usually managed for you when interacting with the ECS but behind the scenes, we are creating SceneMeshes and SceneObjects in Systems.

    If you just want to create the mesh, you can use SceneMesh.meshWithMaterials(). This let's you set the positions, normals, UVs, vertex colors, indices, material ranges, and SceneMaterials as arrays.

    Alternatively, you can create a SceneMesh using our TriangleMesh API (which is used in our Media Player Sample: https://github.com/meta-quest/Meta-Spatial-SDK-Samples/blob/1931b9d04f2e4979f005316127c39e926e44a1fe/MediaPlayerSample/app/src/main/java/com/meta/spatial/samples/mediaplayersample/MediaPlayerSampleActivity.kt#L316). This has a very similar API with meshWithMaterials() but is a little more verbose.

    Attaching the SceneMesh to ECS

    Once we know how to create a SceneMesh, there are a number of ways to add it to our ECS. 
    One of the more simple ways is to use mesh creators. These allow you to create your mesh on-demand when you add a Mesh component to your entity with a matching "mesh://..." URI. For example, in your case, you can register a mesh creator like this:
    // in the app's onCreate()
    registerMeshCreator("mesh://myCustomMesh") {
        SceneMesh.meshWithMaterials(...)
    }
    // later in your code, we create an instance of it
    val myEntity = Entity.create(Mesh(Uri.parse("mesh://myCustomMesh"))

    Alternatively, you can use the MeshManager.setMeshForEntityDirectly() although it is less recommended. Your code would look something like this:

    systemManager
       .findSystem<MeshCreationSystem>()
       .meshManager
       .setMeshForEntityDirectly(myEntity, mySceneMesh)

    Hope this helps!

    3 replies

    davsAnswer
    Meta Employee
    February 6, 2025

    Hi!

    There are a couple of different pieces of this, so I will break them down in two parts.

    Creating a custom SceneMesh

    The SceneMesh is the actual object that represents the geometry that we are rendering. It is usually managed for you when interacting with the ECS but behind the scenes, we are creating SceneMeshes and SceneObjects in Systems.

    If you just want to create the mesh, you can use SceneMesh.meshWithMaterials(). This let's you set the positions, normals, UVs, vertex colors, indices, material ranges, and SceneMaterials as arrays.

    Alternatively, you can create a SceneMesh using our TriangleMesh API (which is used in our Media Player Sample: https://github.com/meta-quest/Meta-Spatial-SDK-Samples/blob/1931b9d04f2e4979f005316127c39e926e44a1fe/MediaPlayerSample/app/src/main/java/com/meta/spatial/samples/mediaplayersample/MediaPlayerSampleActivity.kt#L316). This has a very similar API with meshWithMaterials() but is a little more verbose.

    Attaching the SceneMesh to ECS

    Once we know how to create a SceneMesh, there are a number of ways to add it to our ECS. 
    One of the more simple ways is to use mesh creators. These allow you to create your mesh on-demand when you add a Mesh component to your entity with a matching "mesh://..." URI. For example, in your case, you can register a mesh creator like this:
    // in the app's onCreate()
    registerMeshCreator("mesh://myCustomMesh") {
        SceneMesh.meshWithMaterials(...)
    }
    // later in your code, we create an instance of it
    val myEntity = Entity.create(Mesh(Uri.parse("mesh://myCustomMesh"))

    Alternatively, you can use the MeshManager.setMeshForEntityDirectly() although it is less recommended. Your code would look something like this:

    systemManager
       .findSystem<MeshCreationSystem>()
       .meshManager
       .setMeshForEntityDirectly(myEntity, mySceneMesh)

    Hope this helps!

    Participant
    February 7, 2025

    The meshWithMaterials method also takes the following parameters:
    colors: IntArray (is this 3 or 4 channels; what is the range of each channel)
    materialRanges: IntArray (not sure how this is encoded? must be indexes for materials array?)
    indices: IntArray : are these triangle indices or triangle-strip?
    I also must admit I don't know how to create a simple SceneMaterial (e.g. an unlit yellow surface).

    Meta Employee
    February 8, 2025

    SceneMesh.meshWithMaterials

    • colors: The actual int is represented as ARGB (with the 32 bits represented by AARRGGBB). However, it is usually easier to deal with these with android.graphics.Color which use the same integer format. If you want a good default value to use, you can just use an array of Color.WHITE.
    • materialRanges: This represents a list of pairs (array needs to be a multiple of 2) where the first integer in each pair is the starting index (in the indices array) and the second integer is how many indices to render. Each pair in the array will correspond to one SceneMaterial in the materials array in order. If you are just using one material and all the geometry, you can just use (0, <length of indices array here>).
    • indices : These are just triangle indices. They need to be in multiples of 3 vertices (which define the triangle). If you are having trouble getting things to appear, you may have the winding order wrong. To check, you can utilize SceneMaterial.setSidedness(MaterialSidedness.DOUBLE_SIDED) as by default, we only render front facing triangles.

    Creating SceneMaterials

    There are a number of ways to create a SceneMaterial. The easiest for your case (an unlit yellow surface) may look something like this:

    SceneMaterial(
       SceneTexture(Color.valueOf(Color.YELLOW)),
       shader = SceneMaterial.UNLIT_SHADER
    )

    Here we bind a yellow texture to the base color of the materials and use an unlit shader. If you are using the default shader (PBR shader), you can also use SceneMaterial.setUnlit(true) but it will be slightly less performant.