Hello ImGUI!

I’ve been wanting to add support for ImGUI ever since I started the Vulkan branch about a year ago. To be fair, ImGUI is a pretty easy library to use, yet it depends heavily on dynamic buffers which is something that the new rendering system in Crimild was not providing…

Until now.

Making things more dynamic

ImGUI works by recreating the visual geometries every frame. Which not only means that it depends on dynamic data, but also in the ability to record a new set of rendering commands every frame, which wasn’t supported by the new rendering system at all.

I think I mentioned this before in other posts: the current frame graph implementation in Crimild is static. That is, you create a scene and define which render passes will be used at the beginning of the simulation and then the engine assumes that things won’t change later on. That means nothing can be added to or removed from the scene or frame graph. If something changes, we get an undefined behavior (or more likely, a crash).

A static frame graph might be good enough for demos but it’s definitely not how things are supposed to work in more complex projects (specially games) where new objects are created and destroyed all the time. This has been a known constraint so far, one that made things a lot simpler for me at the beginning. But the time has come to finally remove that limitation for good.

Firstly, Uniform buffers have been dynamic pretty much since the very beginning. Otherwise, no camera will work at all and objects won’t move. They are updated, if needed, just before rendering (there’s still room for optimizations, though). Then, I added support for dynamic vertex buffers when revisiting the particle system a couple of months ago. That should deal with dynamic data, right? (spoiler: no, it doesn’t)

What about command buffers? They are recorded once when creating render passes and then never change. That’s not what we want, so I made some changes here and there and now render buffers have a callback that returns the command buffers every frame. The render pass is completely free to either recreate command buffers or memoize them in order to avoid duplicating the work.

But then, I ran the simulation and the result was not the expected one:

Something else was missing.

It was clear to me that the dynamic buffers were working because the frame counter was being updated as expected. And I was able to add/remove panels, which indicated that command buffers were updated too. What was missing, then? It took me quite a while to find the problem (which is a clear indicator that I need better debugging tools). While uniforms and vertices were being updated, index buffers were not. I assumed that was already supported but it turned out it wasn’t.

Once I figure out the problem, fixing it was pretty easy and then I finally got the correct result.

Handling events

Rendering was fixed, so the next step was to forward mouse events to ImGUI. And that was really, really easy. A few minutes later I had a fully working UI:

Closing Comments

As expected, working with ImGUI was really easy since it’s a very well made library. The problems I have along the way were due lack of support of some the requirements. it’s worth mentioning that ImGUI does provide bindings for Vulkan (and other graphics libraries like OpenGL) but I couldn’t use those since they were not compatible with Crimild. Otherwise, the process would have end up even easier.

See you next time!

Scripting improvements for UI

For my current project (not that one, the other one) I needed a mechanism to create user interfaces in a fast and easy way, without having to position elements explicitly.

In a recent post, I talked about a kind of constraint-based UI layout mechanism included in recent versions of Crimild that allow us to create interfaces in a declarative way:

Each element in the UI defines a set of constraints related with either its parent another element.

Surprisingly, I (almost) didn’t change anything about that (so far). I know, I know, it’s shocking to me as well.

Anyway, that mechanism is pretty powerful, indeed. Still, it requires us to recompile the game every time we make changes (because C++). Therefore, the next evolutionary step was quite obvious: move that feature to Lua.

Using Crimild’s Coding facilities introduced last year, I implemented decoding functions for all of the UI-related components. The result is a Lua script that looks like this:

local UIFrameConstraint = 'crimild.ui.UIFrameConstraint'
local makeConstraint = UIFrameConstraint.Maker
-- Other require calls remove for clarity

-- Create a Restart Button
local restartButton = Group(
{
components = {
UIBackground( { color = { 0.0, 0.0, 1.0, 1.0 } } ),
UIFrame(
{
constraints = {
makeConstraint.after( mainMenuButton ),
makeConstraint.bottom( 10 ),
makeConstraint.width( 80 ),
makeConstraint.height( 20 ),
},
}
),
UILabel( { text = 'Restart' } ),
UIRestartButton(),
},
}
)

Simple and fast. Nice!

BTW, that script is using a new organization for scripts based on Lua modules. But that’s a story for another time…