Creating Meshes

Meshes are representations of the static physical objects that make up the geometry of your game world: buildings, ground, trees, lampposts, etc. Each mesh is like an abstract definition or blueprint for its geometry that you place in your scenes by creating instances with specific translations and rotations in the global space of the scene.

Each mesh is created by the Beast Manager and maintained on its own in the cache. Therefore, each mesh can be instantiated as many times as necessary in any scene that can access that cache, without increasing the amount of memory needed.

All meshes in Beast use triangles as primitives. If you use a different primitive for the geometry in your game, you are responsible for tesselating it into triangles before providing the triangles to Beast.

About triangles and materials

Each triangle that you add to a mesh is created with a material, which you specify only by name. When you create the mesh, you do not yet set up any properties for its materials (like their color, reflectiveness, specularity, etc.). Later, when you instantiate the mesh in a scene, you will need to create materials within that scene whose names match the names of the materials you specified when adding your triangles to the mesh. The materials themselves are stored within the scenes that use them.

This indirect approach to specifying materials has the benefit of allowing you to re-use the same mesh in different scenes while changing the way light reacts to the surface of the mesh in each scene. However, it has the disadvantage that Beast has no way to validate at the time the mesh is created that the named materials will actually exist in the scene at the time the mesh is used. It is your responsibility to ensure that you set up your scene with a set of materials whose names match the names of all materials used in your meshes.

You can also override the material groups in your mesh for each instance that you create. See also Creating Mesh Instances.

Setting up a mesh

To create a mesh:

  1. Create a new ILBMeshHandle.
  2. Initialize the handle by calling ILBBeginMesh(). In your call, you have to specify the Beast Manager that will be responsible for creating and managing the mesh.
  3. Add vertices and their normals to the mesh by calling ILBAddVertexData(). You can call this method multiple times to add vertices in batches; this can help to reduce temporary memory consumption.
  4. Add triangles to your mesh. You add triangles in batches called material groups, each of which collects together multiple triangles that share the same material. To add a material group:
    1. Create the material group by calling ILBBeginMaterialGroup(). In your call, you need to specify the name of the material that you want to apply to the triangles you add.
    2. Add triangles to the group by calling ILBAddTriangleData(). In your call, you need to provide the indices of the vertices that make up the triangles. These indices refer back to the array of vertices you added to the mesh in your calls to ILBAddVertexData(). You can call this method multiple times to add triangles in batches; this can help to reduce temporary memory consumption.
    3. When you are finished adding triangles with the current material, call ILBEndMaterialGroup().
  5. While your mesh is open, you can also add other optional data described in the following sections: UV layers, vertex colors, and tangents/bitangents. You can add these optional groups in any order, but you must end each group before you create another.

    Note that if you plan to bake the lighting for your mesh to a texture, you must create at least one UV layer to tell Beast how to lay out the triangles of the mesh in 2D space.

  6. Finalize the mesh handle by calling ILBEndMesh(). Once you have finalized the mesh, you can no longer modify it, but it will be ready for instantiation in your scenes.

For example, the following code creates a simple mesh:

// Create the mesh
ILBBeastMesh mesh;
ILBBeginMesh(bm, _T("MyMesh"), &mesh);

// Add a batch of vertices and normals
ILBAddVertexData(mesh, points, normals, pointCount);

// Add a batch of triangles with the "Wood" material
ILBBeginMaterialGroup(mesh, "Wood");
ILBAddTriangleData(mesh, vertexIndices_wood, vertexCount_wood);
ILBEndMaterialGroup(mesh);
// Add a second batch of triangles with a different material
ILBBeginMaterialGroup(mesh, "Steel");
ILBAddTriangleData(mesh, vertexIndices_steel, vertexCount_steel);
ILBEndMaterialGroup(mesh);

... // Add optional data (see the following sections)

// Close the mesh
ILBEndMesh(mesh);

Adding UV layers

A UV layer, or UV set, is a customized mapping of each vertex in your mesh into two-dimensional UV space. When you create a texture target or an atlased texture target for a mesh instance, the UV layer is used to determine how the triangles in the 3D mesh get laid out in the 2D texture.

If you plan to bake the lighting for your mesh to a texture, you must set up at least one UV set. In some circumstances, you may want to set up additional UV layers for special effects.

  • By controlling the way your UV layer maps your mesh to 2D space, you can customize the relative resolution of the lightmap for different areas of the mesh. You can create your mapping such that the triangles that need more lighting detail occupy larger areas of the UV space relative to the triangles that need less detail.
  • If your runtime engine has any special requirements for the way it applies UV maps to mesh instances, your UV layer will need to respect those requirements so that your engine will be able to apply the textures created by Beast.
  • UV layers also allow you to send information from selected material channels to different output textures. For example, you could send the diffuse color of a mesh instance to one UV layer, and its specular color to another UV layer. Those UV layers can then be rendered to different textures with different resolutions or different layouts.

To create a UV layer:

  1. While your mesh is open, create the UV layer by calling ILBBeginUVLayer(). In your call, you need to specify a name for the layer.
  2. Call ILBAddUVData() to provide the 2D UV coordinates for each vertex that you added to your mesh in your original calls to ILBAddVertexData(). You must specify the UV coordinates in the same order you added the vertices. You can call ILBAddUVData() multiple times to add coordinates in batches; this can help to reduce temporary memory consumption.
  3. When you have provided UV coordinates for each vertex in the mesh, you can close the UV layer by calling ILBEndUVLayer(). This call will only succeed if you have provided coordinates for each vertex.

For example:

// Add a UV layer.
ILBBeginUVLayer(mesh, "uv1");
ILBAddUVData(mesh, uvCoordinateArray, arrayCount)
ILBEndUVLayer(mesh);

Adding vertex colors

You can specify an RGBA color for each vertex in your mesh.

  • You can use these colors to provide the color for a particular channel in a material. See ILBSetMaterialUseVertexColors().
  • These colors will also be taken into account if you bake a vertex target for an instance of this mesh.

To add a layer of vertex colors:

  1. While your mesh is open, create the color layer by calling ILBBeginColorLayer(). In your call, you need to specify a name for the layer.
  2. Call ILBAddColorData() to provide the color for each vertex that you added to your mesh in your original calls to ILBAddVertexData(). You must specify the colors in the same order you added the vertices. You can call ILBAddColorData() multiple times to add colors in batches; this can help to reduce temporary memory consumption.
  3. When you have provided the color for each vertex in the mesh, you can close the color layer by calling ILBEndColorLayer(). This call will only succeed if you have provided a color for each vertex.

For example:

// Add a color layer.
ILBBeginColorLayer(mesh, "color1");
ILBAddColorData(mesh, rgbaColorArray, arrayCount)
ILBEndColorLayer(mesh);

Adding tangents and bitangents

Some types of render passes, notably the RNM pass, require your meshes to be set up with tangents and bitangents.

To add tangent and bitangent information:

  1. While your mesh is open, open the tangent data layer by calling ILBBeginTangents().
  2. Call ILBAddTangentData() to provide the tangent and bitangent for each vertex that you added to your mesh in your original calls to ILBAddVertexData(). You must specify the data in the same order you added the vertices. You can call ILBAddTangentData() multiple times to add data in batches; this can help to reduce temporary memory consumption.
  3. When you have provided the tangent and bitangent for each vertex in the mesh, you can close the tangent data layer by calling ILBEndTangents(). This call will only succeed if you have provided a tangent and a bitangent for each vertex.

For example:

// Add tangents and bitangents.
ILBBeginTangents(mesh);
ILBAddTangentData(mesh, tangentArray, bitangentArray, arrayCount)
ILBEndTangents(mesh);

Thread safety

You can create multiple different meshes simultaneously in multiple threads. In addition, multiple threads can find meshes in the cache and use them simultaneously. However, only one thread should add data to any given mesh at any time; therefore, each mesh should be created entirely within a single thread and not split across multiple threads.

Related API functions

API functions related to the creation and setup of meshes are declared in the beastmesh.h file.