Examples » Triangle using sokol_gfx

Shows how to use custom renderers with Magnum.

Image

While Magnum provides the GL library as a rendering abstraction, you are not required to use it — it's possible to use a completely custom renderer while taking advantage of all other things that Magnum has, such as math library or asset management APIs. While this example works with standard Magnum installations that include the GL library, it's also possible to build a renderer-less version by toggling WITH_GL and all other options that depend on it off. See Enabling or disabling features for more information.

This example uses the sokol_gfx single-header C library that abstracts OpenGL (ES), D3D and Metal APIs. For simplicity only the OpenGL API together with extension loading using GLEW is used in this example.

The key part here is using just the core Magnum functionality and instructing the Platform::Application class to not do any implicit setup of the GL library by enabling the Configuration::WindowFlag::Contextless flag:

TriangleSokolExample::TriangleSokolExample(const Arguments& arguments):
    Platform::Application{arguments, Configuration{}
        .setTitle("Magnum Triangle using sokol_gfx")
        .setSize(Size)
        .setWindowFlags(Configuration::WindowFlag::Contextless)}
{

Setting this flag means the application acts only as a platform-independent window and event handler and you are responsible for creating your own context (GL, D3D, Vulkan, Metal etc.). A step even further is to not use any Platform::Application class at all (as shown in Triangle using plain GLFW, for example) and use only the Math library, Trade, MeshTools or other things.

After the setup is done, all that remains is calling renderer-specific APIs to set up the buffers, shaders and various state:

    /* Setup sokol_gfx */
    {
        sg_desc desc{};
        sg_setup(&desc);
    }

    /* A vertex buffer */
    {
        const struct TriangleVertex {
            Vector2 position;
            Color3 color;
        } data[]{
            {{-0.5f, -0.5f}, 0xff0000_rgbf}, /* Left vertex, red color */
            {{ 0.5f, -0.5f}, 0x00ff00_rgbf}, /* Right vertex, green color */
            {{ 0.0f,  0.5f}, 0x0000ff_rgbf}  /* Top vertex, blue color */
        };
        sg_buffer_desc desc{};
        desc.content = data;
        desc.size = sizeof(data);
        _vertices = sg_make_buffer(&desc);
    }

    /* A shader */
    {
        sg_shader_desc desc{};
        desc.vs.source = R"GLSL(
#version 330
in vec4 position;
in vec4 color;
out vec4 interpolatedColor;

void main() {
    gl_Position = position;
    interpolatedColor = color;
}
)GLSL";
        desc.fs.source = R"GLSL(
#version 330
in vec4 interpolatedColor;
out vec4 fragmentColor;

void main() {
    fragmentColor = interpolatedColor;
}
)GLSL";
        _shader = sg_make_shader(&desc);
    }

    /* A pipeline state object */
    {
        sg_pipeline_desc desc{};
        desc.shader = _shader;
        desc.layout.attrs[0].name = "position";
        desc.layout.attrs[0].format = SG_VERTEXFORMAT_FLOAT2;
        desc.layout.attrs[1].name = "color";
        desc.layout.attrs[1].format = SG_VERTEXFORMAT_FLOAT3;
        _pipeline = sg_make_pipeline(&desc);
    }
}

And then rendering the scene in the draw event:

void TriangleSokolExample::drawEvent() {
    /* Clear the framebuffer */
    {
        sg_pass_action action{};
        action.colors[0].action = SG_ACTION_CLEAR;
        Color4::from(action.colors[0].val) = 0x1f1f1f_rgbf;
        sg_begin_default_pass(&action, Size.x(), Size.y());
    }

    /* Draw the triangle */
    {
        sg_draw_state state{};
        state.pipeline = _pipeline;
        state.vertex_buffers[0] = _vertices;
        sg_apply_draw_state(&state);
        sg_draw(0, 3, 1);
    }

    sg_end_pass();
    sg_commit();

    swapBuffers();
}

Credits

Source

Full source code is linked below and together with assets also available in the magnum-examples GitHub repository.