Is there anyone who does not want a good graphical user interface? No? Me neither!

But then why isn’t this easier?


ImGUI for the win

It was easy at first. Immediate graphical user interfaces are nice to look at, do not need some big clunky software behind them and are easy to integrate as well. For me, my goto ImGui library, which I use for my private projects is Dear ImGui. Why? I’m used to it by now, and it is one of the best in this category. Maybe I will try to write one myself some day, but until then this will be the one Mango uses.

For the people who don’t know what an immediate graphical user interface is, I’d like to link to this video here. I think it describes the basics really well… and much better than I could do it.

So in theory we are set, we’ve integrated the library and now?

The long road to the property slider

Did I mention that ImGUIs typically are easy to use? So yes, it is true. You could just add a static floating point variable, put it in a function and … You have a slider and you can change the float to any value you want. Thats very simple and for a small, maybe ‘one-file-only’ renderer this would be fine. For Mango the first small issue I had was: How could I possibly access data in an user interface, without exposing all that data to everyone else, too? And how would I determine the type and the sufficient range of the value, while building the GUI? At first I thought if building some more complex (or at least in c++ it is not to straight forward) introspection system which generates type information for structures at compile time. In the end I choose to drop that, it became way too complex really fast and I realized, that it’s definitely not needed at the moment.

For now I settled for something much simpler: We have an ui_system that initializes Dear ImGui, needs some information from the framework and then is responsible for updating and drawing the GUI each frame. The GUI consists of a dock space (when enabled) and prebuild widgets like a material inspector or a scene view. A custom function, rendering a widget can also be submitted at application startup.

On update all these widgets are traversed. They could get access to the application context, so that the widgets can query and modify pretty much everything they need to work. So our property slider for the floating point variable can access the value needed, pass it to the function and it can be changed by it. The only thing missing would be the definition for value ranges. These are generated in advance … by myself ;). Writing rules for them would be a complete overkill, so I simply define the ranges on a value bases. In the end the result counts.


// Material Inspector Traversal in the ui_system.
if (widgets[ui_widget::material_inspector] && material_inspector_enabled && !cinema_view)
{
    if (selected != invalid_entity)
    {
        auto comp = application_scene->query_mesh_component(selected);
        material_inspector_widget(comp, material_inspector_enabled, tmp != selected, selected, rs);
    }
    else
        material_inspector_widget(nullptr, material_inspector_enabled, tmp != selected, selected, rs);
}


image
The material inspector with some supported property-textures.

A complex situation

After planning and implementing most of the widgets, another problem arose. Quaternions! I mean rotations and orientations are easy, right? … Sort of. I actually used to use both quaternions and Euler angles (of course) and spent some time trying to figure out how they work. But most people who are not into programming or math have never heard of them. And to be honest, I don’t think that quaternions belong in a graphical user interface.

Just a quick reminder:

Euler angles have a few problems, for example:

  • Rotation order: XYZ or ZYX or … And each order has a different end result.
  • Singularity problems and the so-called ‘gimbal lock’ problem, which not only leads to possible blockages, but also looks strange when interpolated.

Finally, we do NOT want to use them to represent rotations internally. So we do not do it either!

Quaternions ‘fix’ these issues. But they come with a caveat, they are a number system extending the complex numbers. A quaternion can describe any 3D rotation, whereas it is 4D itself. If you want to know how exactly these work I’d like to mention this video.

Long story short, Mango uses quaternions to describe pretty much everything that involves rotations internally. Additionally, we don’t want to scare people with complex numbers in the GUI, so we use Euler angles to display them and simple to convert into quaternions for internal use. At least until we have some nice rotation interface or gizmos, the current implementation should be fine, even though the reverse operation from internal quaternions to euler angles is a lot harder, so some tricks have to be used.

image
The rotation properties in a transform component.
Values in degrees follow the drag and get reset on release.


/*
 * t_x, t_y and t_z are the previous values so we get the difference in degrees,
 * convert it to radians, produce a quaternion from axis-angle representation
 * and multiply them in the end to our transform rotation, which is also a quaternion.
 */
glm::quat x_quat = glm::angleAxis(glm::radians(x - t_x), glm::vec3(1.0f, 0.0f, 0.0f));
glm::quat y_quat = glm::angleAxis(glm::radians(y - t_y), glm::vec3(0.0f, 1.0f, 0.0f));
glm::quat z_quat = glm::angleAxis(glm::radians(z - t_z), glm::vec3(0.0f, 0.0f, 1.0f));

transform_comp->rotation = x_quat * y_quat * z_quat * transform_comp->rotation;

Happy end?

Where does this leave us then? The GUI looks good, objects can be loaded, images created, entities added, transforms can be moved… But we are not finished yet. Lets call it sufficient for now. New features have to be integrated into the GUI, new widgets should be added, the ECS will not be in the current state forever. The next step, at least for the editor interface, would be to enable loading/saving of scenes (and what about gltf export :)), but these changes will of course lead to internal changes again. Well, I’m happy for the moment, but at the same time still far from the end.



And that concludes this article. You may have found out some interesting things. Images showing the new GUI can be found on GitHub and on the main page.

Hopefully I’ll find some time in the near future to write articles on other topics, including more technical ones. Until then.

image

Paul