Posted on

Mesh Instancing Support Added

With mesh instancing you can draw thousands of copies of a mesh in a single draw call (you can specify an arbitrary number of properties for your instances by attaching per instance data to your mesh to be instanced) without adversely affecting the frame rate. v0.41.8 of the SDK comes with a small demo app (example_Instancing) that demonstrates this feature in the engine and is accessible (along with code) from the editor’s samples browser shown on start up. Here’s a code snippet that illustrates the mesh instancing support:

    
    // ============================================
    // MESH INSTANCING - grid of boxes
    // ============================================

    // the base mesh we will instance
    auto box = static_cast<PrefabricatedMesh*>(firefly::SDK::GetInstance().CreateSceneItem(SceneItemType_BoxSceneItem));
    mainScene->InsertChildAtIndex(*box);

    // per instance transforms (i.e. position, rotation and scale)
    int WIDTH, HEIGHT, DEPTH;
    WIDTH = HEIGHT = DEPTH = 10;
    int INSTANCE_COUNT = WIDTH * HEIGHT * DEPTH;
    transforms = new glm::mat4[INSTANCE_COUNT];
    float meshWidth, meshHeight, meshDepth;
    meshWidth = meshHeight = meshDepth = 4.f;
    float xorig = -((WIDTH  * meshWidth)  / 2.f) + (meshWidth  / 2.f);
    float yorig = -((HEIGHT * meshHeight) / 2.f) + (meshHeight / 2.f);
    float zorig = -((DEPTH  * meshDepth)  / 2.f) + (meshDepth  / 2.f);
    int i = 0;
    for(int x = 0; x < WIDTH; x++)
    {
        float xpos = xorig + (x * meshWidth);
        for(int y = 0; y < HEIGHT; y++)
        {
            float ypos = yorig + (y * meshHeight);
            for(int z = 0; z < DEPTH; z++)
            {
                float zpos = zorig + (z * meshDepth);
                transforms[i++] = glm::translate(glm::mat4(1.f), glm::vec3(xpos, ypos, zpos));
            }
        }
    }

    // tell the mesh the instance count
    box->SetInstanceCount(INSTANCE_COUNT);

    // create an instance data buffer
    auto instanceData = (VertexBuffer*)SDK::GetInstance().CreateVertexBuffer();

    // pack the per-instance data into the data buffer
    instanceData->InitInstanceDataBuffer(INSTANCE_COUNT * sizeof(glm::mat4), &transforms[0]);

    // specify the data layout (instance data -> shader attribute location mappings)
    auto mesh = box->GetMesh();
    mesh->BindVAO();
        instanceData->BindDataBuffer();
        instanceData->SetDataArrayMapping({4, 4, VertexBuffer::DataType::Float, 0, sizeof(glm::mat4), (void*)0});
        instanceData->SetDataArrayMapping({5, 4, VertexBuffer::DataType::Float, 0, sizeof(glm::mat4), (void*)(1 * sizeof(glm::vec4))});
        instanceData->SetDataArrayMapping({6, 4, VertexBuffer::DataType::Float, 0, sizeof(glm::mat4), (void*)(2 * sizeof(glm::vec4))});
        instanceData->SetDataArrayMapping({7, 4, VertexBuffer::DataType::Float, 0, sizeof(glm::mat4), (void*)(3 * sizeof(glm::vec4))});
        instanceData->SetDataArrayDivisor({4, 1});
        instanceData->SetDataArrayDivisor({5, 1});
        instanceData->SetDataArrayDivisor({6, 1});
        instanceData->SetDataArrayDivisor({7, 1});
    mesh->UnbindVAO();

    // attach the per-instance data buffer to the mesh (it will take ownership of the buffer)
    // nb. only needed if you intend to save the instance data along with the mesh otherwise you can destroy it
    // as it's now managed by the VAO
    mesh->Attach(*instanceData);

    // Shader (that supports a per-instance mat4 shader input (attribute) at location 4)
    // nb. vertex_position and vertex_texcoord0 are engine built-in attributes (see shader reference)
    // nb. ProjectionMatrix and ViewMatrix are engine built-in uniforms
    SimpleShader* shader = static_cast<SimpleShader*>(SDK::GetInstance().CreateSceneItem(SceneItemType_SimpleShader));
    shader->SetVertexShader("#version 330 core\n"
                            "layout (location = 0) in vec3 vertex_position;\n"
                            "layout (location = 2) in vec2 vertex_texcoord0;\n"
                            "layout (location = 4) in mat4 instance_matrix;\n"
                            "out vec2 TexCoords;\n"
                            "uniform mat4 ProjectionMatrix;\n"
                            "uniform mat4 ViewMatrix;\n"
                            "void main()\n"
                            "{\n"
                            "    TexCoords = vertex_texcoord0;\n"
                            "    gl_Position = ProjectionMatrix * ViewMatrix * instance_matrix * vec4(vertex_position, 1.0f);\n"
                            "}");
    shader->SetFragmentShader("#version 330 core\n"
                              "out vec4 FragColor;\n"
                              "in vec2 TexCoords;\n"
                              "uniform sampler2D sampler;\n"
                              "void main()\n"
                              "{\n"
                              "    FragColor = texture(sampler, TexCoords);\n"
                              "}");
    shader->Compile();
    shader->Link();
    shader->Bind();
    shader->SetPropertyValueByName("sampler", TextureManager::GetInstance().TextureWithName(GetFireflyDir() + "default_texture.png"));