|
Nuklear
This is a minimal-state, immediate-mode graphical user interface toolkit written in ANSI C and licensed under public domain. It was designed as a simple embeddable user interface for application and does not have any dependencies, a default render backend or OS window/input handling but instead provides a highly modular, library-based approach, with simple input state for input and draw commands describing primitive shapes as output. So instead of providing a layered library that tries to abstract over a number of platform and render backends, it focuses only on the actual UI.
|
=============================================================================
This library was designed to be render backend agnostic so it does not draw anything to screen.
DRAWING
=============================================================================
This library was designed to be render backend agnostic so it does not draw anything to screen directly. Instead all drawn shapes, widgets are made of, are buffered into memory and make up a command queue. Each frame therefore fills the command buffer with draw commands that then need to be executed by the user and his own render backend. After that the command buffer needs to be cleared and a new frame can be started. It is probably important to note that the command buffer is the main drawing API and the optional vertex buffer API only takes this format and converts it into a hardware accessible format.
To draw all draw commands accumulated over a frame you need your own render backend able to draw a number of 2D primitives. This includes at least filled and stroked rectangles, circles, text, lines, triangles and scissors. As soon as this criterion is met you can iterate over each draw command and execute each draw command in a interpreter like fashion:
In program flow context draw commands need to be executed after input has been gathered and the complete UI with windows and their contained widgets have been executed and before calling nk_clear which frees all previously allocated draw commands.
You probably noticed that you have to draw all of the UI each frame which is quite wasteful. While the actual UI updating loop is quite fast rendering without actually needing it is not. So there are multiple things you could do.
First is only update on input. This of course is only an option if your application only depends on the UI and does not require any outside calculations. If you actually only update on input make sure to update the UI two times each frame and call nk_clear directly after the first pass and only draw in the second pass. In addition it is recommended to also add additional timers to make sure the UI is not drawn more than a fixed number of frames per second.
The second probably more applicable trick is to only draw if anything changed. It is not really useful for applications with continuous draw loop but quite useful for desktop applications. To actually get nuklear to only draw on changes you first have to define NK_ZERO_COMMAND_MEMORY and allocate a memory buffer that will store each unique drawing output. After each frame you compare the draw command memory inside the library with your allocated buffer by memcmp. If memcmp detects differences you have to copy the command buffer into the allocated buffer and then draw like usual (this example uses fixed memory but you could use dynamically allocated memory).
Finally while using draw commands makes sense for higher abstracted platforms like X11 and Win32 or drawing libraries it is often desirable to use graphics hardware directly. Therefore it is possible to just define NK_INCLUDE_VERTEX_BUFFER_OUTPUT which includes optional vertex output. To access the vertex output you first have to convert all draw commands into vertexes by calling nk_convert which takes in your preferred vertex format. After successfully converting all draw commands just iterate over and execute all vertex draw commands:
| Function | Description |
|---|---|
| nk__begin | Returns the first draw command in the context draw command list to be drawn |
| nk__next | Increments the draw command iterator to the next command inside the context draw command list |
| nk_foreach | Iterates over each draw command inside the context draw command list |
| nk_convert | Converts from the abstract draw commands list into a hardware accessible vertex format |
| nk_draw_begin | Returns the first vertex command in the context vertex draw list to be executed |
| nk__draw_next | Increments the vertex command iterator to the next command inside the context vertex command list |
| nk__draw_end | Returns the end of the vertex draw list |
| nk_draw_foreach | Iterates over each vertex draw command inside the vertex draw list |
Instead all drawn shapes, widgets are made of, are buffered into memory and make up a command queue. Each frame therefore fills the command buffer with draw commands that then need to be executed by the user and his own render backend. After that the command buffer needs to be cleared and a new frame can be started. It is probably important to note that the command buffer is the main drawing API and the optional vertex buffer API only takes this format and converts it into a hardware accessible format.
To use the command queue to draw your own widgets you can access the command buffer of each window by calling nk_window_get_canvas after previously having called nk_begin:
Important to know if you want to create your own widgets is the nk_widget call. It allocates space on the panel reserved for this widget to be used, but also returns the state of the widget space. If your widget is not seen and does not have to be updated it is '0' and you can just return. If it only has to be drawn the state will be NK_WIDGET_ROM otherwise you can do both update and draw your widget. The reason for separating is to only draw and update what is actually necessary which is crucial for performance.