Skip to main content

Vertex Geometry Builder

Description

The Candera::VertexGeometryBuilder class helps effectively build Candera::VertexGeometry objects.


Procedural Geometry 

Example: Wireframe Cube with LineList

First example shows how to generate a wireframe cube using a Candera::LineList object.

The vertex geometry will be generated procedurally using a Candera::VertexGeometryBuilder object (e.g. m_builder).

Vertex Element Format: Position and Color

At the beginning we have to specify what kind of information will be set in the vertices. Of course we need information about the position and, optionally, for an improved visual effect, we could also add color information.

    // Set the data type to be used: position & color
    m_builder.SetVertexElementFormat(VertexGeometry::Position, 0, VertexGeometry::Float32_3);
    m_builder.SetVertexElementFormat(VertexGeometry::Color, 0, VertexGeometry::Float32_4);
Vertex Data: Position

The position information for the vertices is obtained from the array c_cubeData which, in fact, stores the coordinates of the points A and G. The coordinates for the rest of the vertices are obtained relatively to this points (e.g. F(XA, YG, ZG) or D(XG, YA, ZA)

drawing-4-1676882299.png


// Coordinates for the points A & G
static const Float c_cubeData[2][3] = {
    {-1.0F, -1.0F, -1.0F },
    {1.0F, 1.0F, 1.0F }
};
VertexGeometryBuilder: Set Vertex Elements

Because the LineType property of the LineList object will be set to LineType::Lines, the vertex buffer will have to contain a pair of vertices for each line of the cube: 2 vertices/line * 12 lines = 24 vertices.

    // Set the position & color information for each vertex
    // Line segment AD
    m_builder.SetVertexElement(VertexGeometry::Position, 0, c_cubeData[0][0], c_cubeData[0][1], c_cubeData[0][2]); // A
    m_builder.SetVertexElement(VertexGeometry::Color, 0, 1.F, 0.F, 1.F, 1.F); // magenta
    // Increment the vertex cursor
    m_builder.IncrementVertexCursor();
    m_builder.SetVertexElement(VertexGeometry::Position, 0, c_cubeData[1][0], c_cubeData[0][1], c_cubeData[0][2]); // D
    m_builder.SetVertexElement(VertexGeometry::Color, 0, 1.F, 1.F, 1.F, 1.F); // white
    m_builder.IncrementVertexCursor();
    // Line segment AB
    m_builder.SetVertexElement(VertexGeometry::Position, 0, c_cubeData[0][0], c_cubeData[0][1], c_cubeData[0][2]); // A
    m_builder.SetVertexElement(VertexGeometry::Color, 0, 1.F, 0.F, 1.F, 1.F); // magenta
    m_builder.IncrementVertexCursor();
    m_builder.SetVertexElement(VertexGeometry::Position, 0, c_cubeData[0][0], c_cubeData[0][1], c_cubeData[1][2]); // B
    m_builder.SetVertexElement(VertexGeometry::Color, 0, 1.F, 1.F, 0.F, 1.F); // yellow
    // ...
Create Vertex Buffer

Create a Candera::VertexBuffer object and attach to it the vertex geometry obtained from the Candera::VertexGeometryBuilder:

    VertexGeometry* vertexGeom = m_builder.GetVertexGeometry();
    SharedPointer<VertexBuffer> vertexBuffer = VertexBuffer::Create();
    static_cast<void>(vertexBuffer->SetVertexGeometry(vertexGeom, VertexBuffer::VertexGeometryDisposer::Dispose));
    vertexBuffer->SetPrimitiveType(VertexBuffer::Lines);
Create LineList using the Vertex Buffer

Attach the vertex buffer to LineList object:

    // Create and configure the line list object
    m_lineList = LineList::Create();
    m_lineList->SetAppearance(Appearance::Create());
    m_lineList->GetAppearance()->SetMaterial(Material::Create());
    m_lineList->GetAppearance()->GetMaterial()->SetEmissive(Color(1.0F, 1.0F, 0.0F));
    m_lineList->GetAppearance()->SetShader(m_shaderLightMaterial);
    m_lineList->SetLineType(LineList::Lines);
    m_lineList->SetWidth(1.0);
    m_lineList->SetIntersectionTestEnabled(false);
    m_lineList->SetVertexBuffer(vertexBuffer);
    m_lineList->SetScopeMask(ScopeMask(false));
    static_cast<void>(m_lineList->SetScopeEnabled(1, true));
    m_lineList->SetRenderingEnabled(true);
    m_lineList->SetName("Wireframe");
    static_cast<void>(m_lineList->Upload());
Wireframe Cube Result

The image below shows the resulting wireframe cube:

drawing-4-1676882342.png

For an easier manipulation of the widget in SceneComposer all the shaders are hardcoded in the source file and then created by the widget at the initialization time.

Alternatively obtain shaders from Candera::AssetProvider (e.g. GetAssetProvider()->GetShader("...")) in case the application uses an asset library.


Indexed Geometry 

Example: Solid Cube from one Triangle Strip

The next example will show how to create a solid cube from one triangle strip using a Candera::Mesh object. In this case the cube is determined by only 14 vertices which have to be ordered in a special way.

The figure below presents a cube unfolded and a possible solution of ordering the vertices:

drawing-4-1676882414.png

Indexed Vertex Sequence

The vertex sequence is stored in the c_index_order array, encoding the order:

  • G->C->F->B->A->C->D->H->A->E->F->H->G->C.

Each array element represents an offset it the vertex buffer.

// The vertex sequence - triangle strip
static const UInt16 c_index_order[] = { 12, 11, 7, 3, 0, 11, 1, 17, 0, 5, 7, 17, 12, 11 };

The vertex geometry builder object used in the previous example has already 24 vertices set with position and color information. Because only 14 vertices out of 24 are needed in this case and, also, because their sequence has to be different, an index buffer is added:

    // Change the order of the vertices using indexes
    for (Int index = 0; index < 14; index++) {
        // Define the value of the index
        m_builder.SetIndexElement(c_index_order[index]);
        // Increment the index cursor
        m_builder.IncrementIndexCursor();
    }
Create TriangleStrip Vertex Buffer

Create the vertex buffer using the geometry from the builder:

    VertexGeometry* vertexGeom = m_builder.GetVertexGeometry();
    SharedPointer<VertexBuffer> vertexBuffer = VertexBuffer::Create();
    static_cast<void>(vertexBuffer->SetVertexGeometry(vertexGeom, VertexBuffer::VertexGeometryDisposer::Dispose));
    vertexBuffer->SetPrimitiveType(VertexBuffer::TriangleStrip);
Create Mesh using the Vertex Buffer

Configure mesh and attach the vertex buffer:

    m_mesh = Mesh::Create();
    m_mesh->SetVertexBuffer(vertexBuffer);
    m_mesh->SetAppearance(appearance);
    m_mesh->SetRenderingEnabled(false);
    m_mesh->SetName("SolidNonTextured");
    static_cast<void>(m_mesh->Upload());
Solid Cube Result

The image below shows the resulting solid cube:

drawing-4-1676882451.png



Remove & Add Vertex Elements 

Example: Textured Cube

In this example the vertex geometry builder object will be used to generate the vertex geometry for a textured cube.

Adding texture to a mesh requires that its vertices should contain specific information like normal and texture coordinate. Also, the color information is no longer needed so it can be removed:

    // Vertex Elements Manipulation
    // Remove color data associated to the vertices
    m_builder.RemoveVertexElement(VertexGeometry::Color,0);
    // Add data type to be used: texture coordinate & normal
    m_builder.SetVertexElementFormat(VertexGeometry::TextureCoordinate, 0, VertexGeometry::Float32_2);
    m_builder.SetVertexElementFormat(VertexGeometry::Normal, 0, VertexGeometry::Float32_3);
Vertex Data: Normals and Texture Coordinates

After adding the new data types for the vertices it is time to add the corresponding information. First, add normals for vertices:

    // Move the cursor to the first vertex
    m_builder.SetVertexCursor();
    for (UInt32 i = 0; i < m_builder.GetVertexCount(); i++ )
    {
        m_builder.SetVertexElement(VertexGeometry::Normal, 0, 0.0F, 0.0F, 1.0F);
        m_builder.IncrementVertexCursor();
    }

In order to map the UV texture coordinate values correctly, the cube faces should be drawn using 4 vertices per face, so, in this case, all 24 vertices will be used. Of course, the vertex order has to be adjusted.

The new vertex order is stored in the c_indexDataTexturedCube array.

// Vertex order - textured cube
static const UInt16 c_indexDataTexturedCube[24] = {
    1,17,0,5,
    2,6,3,7,
    9,8,11,12,
    13,14,19,21,
    15,23,16,22,
    4,10,20,18
};

Now, reorder the vertices:

    // Overwrite the index buffer
    // Move the cursor to the first index
    m_builder.SetIndexCursor();
    for (UInt32 i = 0; i < m_builder.GetVertexCount(); i++ )
    {
        // Define the value of the index
        m_builder.SetIndexElement(c_indexDataTexturedCube[i]);
        m_builder.IncrementIndexCursor();
    }

The vertices of each face of the cube should be set with texture coordinate information as shown below:

drawing-4-1676882513.png



Set the texture coordinates for vertices:

    Int idx = 0;
    for (Int nb_of_faces = 0; nb_of_faces<6; nb_of_faces++) {
        for (Int u=0; u<=1; u++) {
            for (Int v=0; v<=1; v++) {
                //Set the position of the vertex cursor
                m_builder.SetVertexCursor(c_indexDataTexturedCube[idx]);
                m_builder.SetVertexElement(VertexGeometry::TextureCoordinate, 0, static_cast<Float>(u), static_cast<Float>(v));
                idx++;
            }
        }
    }

The vertex buffer is created and configured in the same way as in the previous example.

The mesh setup is also similar but, unlike the solid cube, the textured cube appearance has to be set with a texture and a texture shader.

Textured Cube Result

Here is the final result:

drawing-4-1676882532.png



Range Data Usage 

Example: Solid Cube using Range Data

This example will show how to create a solid cube as in the second example but using range data.

First, all existing data from the vertex geometry builder is removed.

    // Clear all data stored by the builder
    m_builder.Clear();

The vertex data is taken from the two buffers c_positionData and c_colorData.

static const Float c_positionData[8][3] = {
    { -1.0F, 1.0F, 1.0F},
    { 1.0F, 1.0F, 1.0F},
    { -1.0F, -1.0F, 1.0F},
    { 1.0F, -1.0F, 1.0F},
    { -1.0F, 1.0F, -1.0F},
    { 1.0F, 1.0F, -1.0F},
    { -1.0F, -1.0F, -1.0F},
    { 1.0F, -1.0F, -1.0F}
};

static const Float c_colorData[8][4] = {
    { 0.0F, 0.0F, 1.0F, 1.0F},
    { 0.0F, 1.0F, 0.0F, 1.0F},
    { 0.0F, 1.0F, 1.0F, 1.0F},
    { 1.0F, 0.0F, 0.0F, 1.0F},
    { 1.0F, 0.0F, 1.0F, 1.0F},
    { 1.0F, 1.0F, 0.0F, 1.0F},
    { 1.0F, 1.0F, 1.0F, 1.0F},
    { 1.0F, 0.5F, 1.0F, 1.0F}
};

The vertex sequence is taken from the array c_indexData.

// Range data index
static const UInt16 c_indexData[] = {3, 2, 1, 0, 4, 2, 6, 3, 7, 1, 5, 4, 7, 6 };

Use the methods Candera::VertexGeometryBuilder::SetVertexElementRange and Candera::VertexGeometryBuilder::SetIndexElementRange to add the required information.

    // Add position, color and an index from range data
    // Splice values to the position buffer starting with the first vertex
    static_cast<void>(m_builder.SetVertexElementRange(VertexGeometry::Position, 0, 0, VertexGeometry::Float32_3,
        8, 3*sizeof(Float), c_positionData ));
    // Splice values to the color buffer starting with the first vertex
    static_cast<void>(m_builder.SetVertexElementRange(VertexGeometry::Color, 0, 0, VertexGeometry::Float32_4,
        8, 4*sizeof(Float), c_colorData ));

    // Splice values to the index buffer starting with the first index
    static_cast<void>(m_builder.SetIndexElementRange(0, sizeof(c_indexData)/sizeof(UInt16),c_indexData));

The vertex buffer configuration and the mesh setup are identical with the ones from the second example.

Here is the final result:

drawing-4-1676882596.png


Concatenate Geometries 

VertexGeometryBuilder::SpliceGeometry

In some cases it might be useful to concatenate two vertex geometries. This can be done by using the Candera::VertexGeometryBuilder::SpliceGeometry method as shown below:

    m_builder.SpliceGeometry(0, 0, vertexGeometry1);
    m_builder.SpliceGeometry(vertexGeometry1->GetVertexCount(), vertexGeometry1->GetIndexCount(), vertexGeometry2);
    vertexGeometry1cat2 = builder.GetGeometry();