Examples » Engine » ImGui

Using the ImGui integration plugin and cubos::engine::DataInspector resource.

The ImGui plugin allows you to integrate the Dear ImGui library with Cubos, making it quick, straightforward, and effortless to create user interfaces and interactive debugging tools within your applications.

Image

First, let's start by including the header files we need.

#include <iostream>
#include <map>
#include <vector>

#include <imgui.h>

#include <cubos/core/reflection/external/map.hpp>
#include <cubos/core/reflection/external/primitives.hpp>
#include <cubos/core/reflection/external/string.hpp>
#include <cubos/core/reflection/external/vector.hpp>
#include <cubos/core/reflection/reflect.hpp>
#include <cubos/core/reflection/traits/constructible.hpp>
#include <cubos/core/reflection/traits/fields.hpp>
#include <cubos/core/reflection/traits/nullable.hpp>

#include <cubos/engine/imgui/data_inspector.hpp>
#include <cubos/engine/imgui/plugin.hpp>
#include <cubos/engine/render/target/plugin.hpp>
#include <cubos/engine/settings/plugin.hpp>
#include <cubos/engine/window/plugin.hpp>

Then, make sure to add the plugin.

    cubos.plugin(settingsPlugin);
    cubos.plugin(windowPlugin);
    cubos.plugin(renderTargetPlugin);
    cubos.plugin(imguiPlugin);

Once the ImGui plugin is added, you can create systems to display ImGui windows and widgets. Here's a system which opens an ImGui window, and its demo.

    cubos.system("show ImGui demo").tagged(imguiTag).call([]() {
        ImGui::Begin("Dear ImGui + Cubos");
        ImGui::Text("Hello world!");
        ImGui::End();

        ImGui::ShowDemoWindow();
    });

Ensure that you add your system with the cubos.imgui tag; otherwise, the ImGui elements from that system won't be be visible.

Pretty simple right? You're now equipped to craft and utilize ImGui's functions to design your cool user interface.

Now, we'll also show you how you can use the cubos::engine::DataInspector resource in combination with ImGui integration plugin to inspect/modify data in real time.

To start off, we'll need to have some sort of dummy data shared across our application, so we can inspect/modify later. Let's create a DummyResource with some fields and fill it with random data.

struct Person
{
    CUBOS_REFLECT;
    std::string name;
    int32_t age;
    float weight;
    bool dead;
};

CUBOS_REFLECT_IMPL(Person)
{
    return Type::create("Person")
        .with(FieldsTrait()
                  .withField("name", &Person::name)
                  .withField("age", &Person::age)
                  .withField("weight", &Person::weight)
                  .withField("dead", &Person::dead))
        .with(NullableTrait{[](const void* instance) {
                                const auto* person = static_cast<const Person*>(instance);
                                return person->dead;
                            },
                            [](void* instance) {
                                auto* person = static_cast<Person*>(instance);
                                person->dead = true;
                            }})
        .with(ConstructibleTrait::typed<Person>().withDefaultConstructor().build());
}

struct DummyResource
{
    CUBOS_REFLECT;

    int integer;
    Person person;
    std::vector<Person> persons;
    std::vector<int32_t> vec;
    std::map<int32_t, int32_t> map;
};

CUBOS_REFLECT_IMPL(DummyResource)
{
    return Type::create("DummyResource")
        .with(FieldsTrait()
                  .withField("integer", &DummyResource::integer)
                  .withField("person", &DummyResource::person)
                  .withField("persons", &DummyResource::persons)
                  .withField("vec", &DummyResource::vec)
                  .withField("v", &DummyResource::map))
        .with(ConstructibleTrait::typed<DummyResource>().withDefaultConstructor().build());
}
    cubos.resource<DummyResource>(
        DummyResource{.integer = 1337,
                      .person = Person{"roby", 1337, 666.5F, false},
                      .persons{Person{"roby", 1337, 666.5F, false}, Person{"riscado", 123, 321.0F, false}},
                      .vec = {12, 59, 25},
                      .map = {
                          {1, 2},
                          {2, 4},
                          {3, 6},
                          {4, 8},
                      }});

Well now, using the cubos::engine::DataInspector is pretty easy, all you have to do is access the resource on your system, and use the functions DataInspector::edit and DataInspector::edit.

    cubos.system("data inspector example").tagged(imguiTag).call([](DataInspector& inspector, DummyResource& data) {
        ImGui::Begin("Data Inspector");
        inspector.edit(data);
        ImGui::End();
    });
Image

You can find more about how to use Dear ImGui stuff here.