Search Results for

    Show / Hide Table of Contents

    Meshes

    Meshes

    Introduction

    A mesh is a single drawable element, a fundamental structure used to represent 3D objects in a scene. It is composed of vertices (points in 3D space) that are connected to form polygons, typically triangles. It also is rendered with only one Material.

    Each vertex in a mesh can hold additional information, such as normals (which define the direction a surface is facing), UV coordinates (which map textures to the object), and color or other custom data. This allows the mesh to have detailed surface characteristics, like how it reflects light or how a texture is applied to it.

    Complex models contains multiple meshes with multiple materials, as defined in this article.

    Parameters

    The Evergine mesh class contains the following parameters.

    Property Type Description
    VertexBuffers VertexBuffer[]. Array containing the vertex buffers of the mesh (explained here)
    IndexBuffer IndexBuffer. The mesh index buffer. Can be null (explained here)
    Buffers Buffer[]. Array containing all the Buffer instances of every vertex buffer.
    Offsets int[]. Array containing all the vertex offsets of every vertex buffer.
    PrimitiveTopology PrimitiveTopology. Enumeration containing the type of geometric topology used for the mesh. Its values are:
  • Undefined
  • PointList
  • LineList
  • LineStrip
  • TriangleList
  • TriangleStrip
  • LineListWithAdjacency
  • TriangleListWithAdjacency
  • Patch_List
  • InputLayouts InputLayouts. Object containing the LayoutDescription of every vertex buffer.
    Primitive Count int. The number of primitives of the mesh.
    ElementCount int. The number of elements in the mesh. It's affected by the topology (For example, 1 triangle contains 3 elements, but 1 line is made of 2).
    VertexSize ushort. The memory size (in bytes) of a single vertex.
    AllowBatching bool. If the mesh can be used in dynamic and static batching.

    Vertex Buffers

    The VertexBuffer class in Evergine represents a buffer that holds the vertex data required for rendering 3D objects. It mainly contains the Buffer that contains the raw data and the LayoutDescription declaring what kind of vertex information it has. The Vertex Buffer plays a crucial role in managing the vertex layouts and size, ensuring that this data can be processed efficiently by the GPU. The VertexBuffer class handles data organization and allows developers to define how vertex attributes like positions, normals, and textures are stored.

    Interleaved and Non-Interleaved Data

    It's common to think of a vertex as a single entity (an object that holds all its relevant data). However, we can also get vertex attributes from separate streams, where the data for each attribute (like position, normals, or colors) is stored in contiguous memory blocks: one for positions, another for normals, and so on.

    This approach offers several advantages. For example, it allows vertex data to be accessed by different Vertex Shaders without wasting bandwidth or cache space. In cases where a vertex is processed for a shadow map, the vertex function might only require the position, not the other attributes. If all vertex data is stored together, the GPU would have to traverse more memory to retrieve only the needed data. Additionally, optimizing the layout of this data within a struct can be tricky due to alignment and cache considerations.

    Interleaved Vertex Attributes

    Interleaved

    Non-interleaved Vertex Attributes

    Non-Interleaved

    As we can see, vertex data can be arranged either with all attributes of a vertex stored together (interleaved) or with each attribute type stored together across all vertices (non-interleaved).

    Parameters

    Property Type Description
    Buffer Buffer. Buffer containing the vertex data. The buffer must contain the flag BufferFlag.VertexBuffer. Otherwise it won't be accepted.
    Size int. Size of the buffer in bytes.
    Offset int. The offset in bytes of the buffer data.
    Data IntPtr. Pointer of the raw memory data of the buffer.
    LayoutDescription LayoutDescription. Description of the different ElementDescription instances, explaining the type of information, semantics, stride and offsets the vertex contains, allowing to properly extract the data.
    VertexCount int. The number of vertices to be fetched from the buffer.

    Predefined Vertices structures.

    In order to make it easier for developers, Evergine contains a set of structs that can be used to define vertices with different kind of information. Our predefine types are:

    Type Description
    VertexPosition Struct of vertex containing only Position.
    VertexPositionColor Struct of vertex containing the properties Position and Color.
    VertexPositionColorDualTexture Struct of vertex containing the properties Position, Color, TexCoord and TexCoord2.
    VertexPositionColorTexture Struct of vertex containing the properties Position, Color and TexCoord.
    VertexPositionDualTexture Struct of vertex containing the properties Position, TexCoord and TexCoord2.
    VertexPositionNormal Struct of vertex containing the properties Position and Normal.
    VertexPositionNormalColor Struct of vertex containing the properties Position, Normal and Color.
    VertexPositionNormalColorDualTexture Struct of vertex containing the properties Position, Normal, Color, TexCoord and TexCoord2.
    VertexPositionNormalColorTexture Struct of vertex containing the properties Position, Normal, Color and TexCoord.
    VertexPositionNormalDualTexture Struct of vertex containing the properties Position, Normal, TexCoord and TexCoord2.
    VertexPositionNormalTangentColorDualTexture Struct of vertex containing the properties Position, Normal, Tangent, Color, TexCoord and TexCoord2.
    VertexPositionNormalTangentTexture Struct of vertex containing the properties Position, Normal, Tangent, Color, and TexCoord.
    VertexPositionNormalTexture Struct of vertex containing the properties Position, Normal and TexCoord.
    VertexPositionTexture Struct of vertex containing the properties Position and TexCoord.

    The advantage of using this structures is that they provide the LayoutDescription of that vertex. However, you can create your own VertexBuffers with your own arrays, but you will have to build the LayoutDescription.

    Index Buffers

    3D meshes you'll be rendering often have vertices that are shared across multiple triangles. This occurs even with simple shapes, like a rectangle:

    Rendering a rectangle requires two triangles, which would normally mean a vertex buffer containing 6 vertices. However, two of these vertices would need to be duplicated, resulting in a 50% redundancy. This issue becomes more pronounced with complex meshes, where a single vertex is typically shared among 3 triangles. To solve this inefficiency, we use an index buffer.

    An index buffer is essentially a list of references to the vertices in the vertex buffer. This allows for rearranging the vertex data and reusing vertices across multiple triangles without duplicating data. In the example of a rectangle, if the vertex buffer contains four unique vertices, the index buffer would reference these vertices, with the first three indices forming the top-right triangle, and the last three creating the bottom-left triangle.

    Parameters

    Property Type Description
    Buffer Buffer. Buffer containing the indices data. The buffer must contain the flag BufferFlag.IndexBuffer. Otherwise it won't be accepted.
    Size int. Size of the buffer in bytes.
    Offset int. The offset in bytes of the buffer data.
    Data IntPtr. Pointer of the raw memory data of the buffer.
    IndexFormat IndexFormat. The type of index. Can be Uint16 (unsigned short) or UInt32 (unsigned int).
    FlipWinding bool. If true, the triangle side is defined in counter clock-wise order.
    IndexCount int. Number of indices.

    Create Mesh from Code

    Simple mesh using a predefined type

    The next code explain how to create a simple mesh. It uses the VertexPositionColor struct for defining its data:

    // Vertices and indices data.
    ushort[] indexData = new ushort[] { 0, 1, 2, 0, 2, 3 };
    
    VertexPositionColor[] vertexData = new VertexPositionColor[]
    {
        new VertexPositionColor(new Vector3(-0.5f, 0.5f, 0.0f), Color.Blue),
        new VertexPositionColor(new Vector3(0.5f, 0.5f, 0.0f), Color.Red),
        new VertexPositionColor(new Vector3(0.5f, -0.5f, 0.0f), Color.Green),
        new VertexPositionColor(new Vector3(-0.5f, -0.5f, 0.0f), Color.Yellow),
    };
    
    // Vertex Buffer
    var vBufferDescription = new BufferDescription()
    {
        SizeInBytes = (uint)Unsafe.SizeOf<VertexPositionColor>() * (uint)this.vertexData.Length,
        Flags = BufferFlags.ShaderResource | BufferFlags.VertexBuffer,
        Usage = ResourceUsage.Default
    };
    
    // We create the buffer using the vertex array data previously defined.
    Buffer vBuffer = graphicsContext.Factory.CreateBuffer(this.vertexData, ref vBufferDescription);
    VertexBuffer vertexBuffer = new VertexBuffer(vBuffer, VertexPositionColor.VertexFormat);
    
    // Index Buffer
    var iBufferDescription = new BufferDescription()
    {
        SizeInBytes = (uint)(sizeof(ushort) * this.indexData.Length),
        Flags = BufferFlags.IndexBuffer,
        Usage = ResourceUsage.Default,
    };
    
    // We create the buffer using the ushort array data previously defined.
    Buffer iBuffer = graphicsContext.Factory.CreateBuffer(this.indexData, ref iBufferDescription);
    var indexBuffer = new IndexBuffer(iBuffer);
    
    // Create Mesh using the previously defined vertex buffer and index buffer.
    return new Mesh(new VertexBuffer[] { vertexBuffer }, indexBuffer, PrimitiveTopology.TriangleList)
    {
        BoundingBox = this.ComputeBoundingBox(),
    };
    

    Simple mesh using your own arrays

    This code, on the other hand, explain how to build your own mesh with your own data types.

    // Indices data array.
    ushort[] indexData = new ushort[] { 0, 1, 2, 0, 2, 3 };
    
    // Position data array.
    Vector3[] positions = new Vector3[]
    {
        new Vector3(-0.5f, 0.5f, 0.0f),
        new Vector3(0.5f, 0.5f, 0.0f),
        new Vector3(0.5f, -0.5f, 0.0f),
        new Vector3(-0.5f, -0.5f, 0.0f),
    };
    
    // Color data array.
    Vector4[] colors = new Vector4[]
    {
        Color.Blue.ToVector4(),
        Color.Red.ToVector4(),
        Color.Green.ToVector4(),
        Color.Yellow.ToVector4(),
    };
    
    // Vertex Buffer with the position attribute data.
    var bufferPosDesc = new BufferDescription()
    {
        SizeInBytes = (uint)Unsafe.SizeOf<Vector3>() * (uint)this.positions.Length,
        Flags = BufferFlags.ShaderResource | BufferFlags.VertexBuffer,
        Usage = ResourceUsage.Default,
    };
    
    Buffer bufferPos = graphicsContext.Factory.CreateBuffer(this.positions, ref bufferPosDesc);
    LayoutDescription layoutPos = new LayoutDescription().Add(new ElementDescription(ElementFormat.Float3, ElementSemanticType.Position, 0, 0));
    VertexBuffer vertexBufferPos = new VertexBuffer(bufferPos, layoutPos);
    
    // Vertex Buffer with the color attribute data.
    var bufferColorDesc = new BufferDescription()
    {
        SizeInBytes = (uint)Unsafe.SizeOf<Vector4>() * (uint)this.colors.Length,
        Flags = BufferFlags.ShaderResource | BufferFlags.VertexBuffer,
        Usage = ResourceUsage.Default,
    };
    
    Buffer bufferColor = graphicsContext.Factory.CreateBuffer(this.colors, ref bufferColorDesc);
    LayoutDescription layoutColor = new LayoutDescription().Add(new ElementDescription(ElementFormat.Float4, ElementSemanticType.Color, 0));
    VertexBuffer vertexBufferColor = new VertexBuffer(bufferColor, layoutColor);
    
    // Index Buffer
    var iBufferDescription = new BufferDescription()
    {
        SizeInBytes = (uint)(sizeof(ushort) * this.indexData.Length),
        Flags = BufferFlags.IndexBuffer,
        Usage = ResourceUsage.Default
    };
    
    Buffer iBuffer = graphicsContext.Factory.CreateBuffer(this.indexData, ref iBufferDescription);
    var indexBuffer = new IndexBuffer(iBuffer);
    
    // Create Mesh with the 2 previous vertex buffers and the index buffer.
    return new Mesh(new VertexBuffer[] { vertexBufferPos, vertexBufferColor }, indexBuffer, PrimitiveTopology.TriangleList)
    {
        BoundingBox = this.ComputeBoundingBox(),
    };
    

    In both cases the result will be the same:

    Quad

    Note

    In this article is explained how to show this custom mesh into a new entity.

    For a single mesh Model, Evergine offers an easy way creating it, just passing it as a parameter in its constructor:

    In this article
    Back to top
    Generated by DocFX