Examples » Triangle using plain GLFW

Shows how to use Magnum with custom platform integration APIs.

Image

This example has equivalent output to the Triangle example, but demonstrates that it's possible to sidestep the Platform::Application wrappers and hook into existing OpenGL context and window surface provided by a third-party library.

In this case we're using GLFW. It also has a Magnum wrapper in Platform::GlfwApplication, but here the point is to show how to use Magnum with third-party window and event handling, so we won't use it. Note that it's possible to use any windowing toolkit that provides an OpenGL context, GLFW was chosen because it has a very clear and simple API.

We need to have GL::Buffer, GL::Mesh and Shaders::VertexColor included to populate, set up and render our triangle and GL::DefaultFramebuffer to properly clear the framebuffer before rendering. The Platform::GLContext class will take care of hooking into provided OpenGL context.

Because Magnum has its own stripped-down OpenGL headers, GLFW needs to be included last so its GL headers don't take priority over the Magnum ones.

#include <Magnum/GL/Buffer.h>
#include <Magnum/GL/DefaultFramebuffer.h>
#include <Magnum/GL/Mesh.h>
#include <Magnum/Platform/GLContext.h>
#include <Magnum/Shaders/VertexColor.h>
#include <GLFW/glfw3.h>

The main() function preamble is not much different from the GLFW example code:

int main(int argc, char** argv) {
    /* Initialize the library */
    if(!glfwInit()) return -1;

    /* Create a windowed mode window and its OpenGL context */
    GLFWwindow* const window = glfwCreateWindow(
        800, 600, "Magnum Plain GLFW Triangle Example", nullptr, nullptr);
    if(!window) {
        glfwTerminate();
        return -1;
    }

    /* Make the window's context current */
    glfwMakeContextCurrent(window);

Now that we have the OpenGL context created and made current, we can instantiate Platform::GLContext. We'll do it in an isolated scope, so the RAII principle will take care of destroying it at a right time. We'll pass the argc and argv parameters to it — it's completely optional and we could use the default Platform::GLContext::GLContext() constructor instead, but this allows us to control Magnum renderer behavior on particular hardware from command-line. All Magnum options are prefixed with --magnum- to avoid conflicts with other application arguments and even if we wouldn't propagate them, it's still possible to set the options from environment. See Command-line options for more information.

After that follows setup of the triangle mesh that's equivalent to what was done in the Triangle example. Finally there's the main application loop that performs drawing and polls for updates.

    {
        /* Create Magnum context in an isolated scope */
        Platform::GLContext ctx{argc, argv};

        /* Setup the colored triangle */
        using namespace Math::Literals;

        struct TriangleVertex {
            Vector2 position;
            Color3 color;
        };
        const TriangleVertex 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 */
        };

        GL::Buffer buffer;
        buffer.setData(data, GL::BufferUsage::StaticDraw);

        GL::Mesh mesh;
        mesh.setPrimitive(GL::MeshPrimitive::Triangles)
            .setCount(3)
            .addVertexBuffer(buffer, 0,
                Shaders::VertexColor2D::Position{},
                Shaders::VertexColor2D::Color{Shaders::VertexColor2D::Color::Components::Three});

        Shaders::VertexColor2D shader;

        /* Loop until the user closes the window */
        while(!glfwWindowShouldClose(window)) {

            /* Render here */
            GL::defaultFramebuffer.clear(GL::FramebufferClear::Color);
            mesh.draw(shader);

            /* Swap front and back buffers */
            glfwSwapBuffers(window);

            /* Poll for and process events */
            glfwPollEvents();
        }
    }

Once the window closes, Magnum context gets destroyed at the end of scope. The last remaining thing before exiting from main() is to terminate GLFW.

    glfwTerminate();
}

Compared to the Triangle example, the compilation is a bit trickier, as we need to link to a correct OpenGL context handler depending on the platform. In this case we'll limit the platforms to what GLFW supports, but see Using custom platform toolkits for further information.

find_package(GLFW REQUIRED)

find_package(Magnum REQUIRED GL Shaders)
if(CORRADE_TARGET_APPLE)
    find_package(Magnum REQUIRED CglContext)
elseif(CORRADE_TARGET_UNIX)
    find_package(Magnum REQUIRED GlxContext)
elseif(CORRADE_TARGET_WINDOWS)
    find_package(Magnum REQUIRED WglContext)
else()
    message(FATAL_ERROR "No context handler available on this platform")
endif()

set_directory_properties(PROPERTIES CORRADE_USE_PEDANTIC_FLAGS ON)

add_executable(magnum-triangle-plain-glfw main.cpp)
target_link_libraries(magnum-triangle-plain-glfw PRIVATE
    GLFW::GLFW
    Magnum::GL
    Magnum::GLContext
    Magnum::Magnum
    Magnum::Shaders)

That's it! The full file content is linked below. Full source code is also available in the magnum-examples GitHub repository.