Now that my scripting language appears to be complete, it’s back onto the UI programming. Most of it is there, one thing remaining is the “Flowchart”.
It’s not exactly a flowchart widget, more like a “nodes” widget.
The idea is the eventually you’d be able to add new nodes, connect and disconnect them as you see fit. Each node also contains a function; the purpose if that you can send data from one end to the other; much like my uncompleted noise-machine.
Eventually, you’d be able to use it for things like displaying a tech-tree; except the Player won’t be able to modify it.
Anyway; it’s not clear from a screenshot, but that above runs at about 25fps on my machine. Completely unacceptable. To be fair there is about 198 individual rectangle draw calls there… it doesn’t seem like it should be slowing things down; but it is.
I need to look into why it’s slowing things down so much; and if it’s just because I’m hitting a bandwidth limit; then it’s time to look into batching the draw calls (which I really should have done from day 1). Hopefully the entire UI can be knocked down to a single draw call. Or if I think about it correctly, maybe I could draw just to a texture, or individual widgets as textures to save some calls.
As I was continuing to work on tools for the engine, I wondered why I’m not using an “in-engine” tool. Other engines often have things like the level editor in engine so as to avoid duplicate code-bases.
I looked at my work on Noise Factory and realised that I had built a wrapper around my noise library and then plugged that into a GUI API. Why do that when I could’ve just used the UI I have made in-engine?
Quickly I realised it’s because my UI wasn’t easy to use to put together a program. The GUI framework tricks I learnt from wxWidgets really showed me the flaws in my own design. So, I’m not re-working the UI library to be friendlier to use, and easier to adjust.
This means, that I’ll most likely spend another few weeks working on the UI library. I guess if it’s at a point where I can use it quickly and easily, it will be that much simpler to finally implement it in a game.
Fleet Battles is what I’m naming the miniature game that I’m making to realise the fleet warfare aspect of the game. I’m also doing it so that I actually complete something and hopefully gain some learning from it to then proceed with the rest of The Last Boundary.
I’m sort of thinking that I may end up with several games, but that’s far into the future.
Anyway, I decided that the UI I had created looked a little sad; so I’ve been working in GIMP to make a new one. The result required some reworking of my UI code to incorporate the new bits and bobs but I think it looks pretty good so far.
The title could probably use some work; but I’m far from a designer. So it’ll probably stay like that.
I’ve done a bit of refactoring of my UI code and brought it down a few hundred lines of code.
I’ve also started noting the performance. Right now, a window with a table and a bit of text and another window with just text gets me around 500-550 fps. This is from a base of about 2500 (if nothing is being drawn to the screen).
I have no idea if this is acceptable or not. In contrast, my scene with a sun and an earth runs at about 200-250 fps. The thing is; I’m doing things in the most inefficient way.
So; I put some work into speeding up the code. Everything in the UI uses a single shader for one. Next is to get everything to use a single VBO; it’s not doing that now because my text quads are drawn with position info in the vertices, whereas everything else uses a translation and scale matrix to define the position.
If they both use the translation matrix, I can remove any need to bind different buffers between calls. This should make things faster.
Also, everything at the moment binds it’s texture. However everything uses the same texture (except text); so I may be able to remove the
glBindTexture calls for a lot of the time. Probably via a texture manager or something.
If it’s still not fast enough; I’ve been reading about instance drawing and also about using textures to hold position info (R,G is x,y and B,A is w,h). I think I can combine these so that my
gl_InstanceID in the vertex shader is my lookup into the texture of position info. I’d probably also need a second texture to hold UV coords seeing as these don’t change much either (textures rather than providing a lookup array to the shader, only because that would need to be updated and passed each frame anyway).
I’ve been without the internet for a little while so I spent a good part of that time refactoring some of the UI code.
I quickly identified a distinction between widgets that hold a single other widget, and widgets that contain many other widgets. These are Containers vs Layouts. Obviously a widget that holds one other widget is a container, something that holds many widgets only does so for the sole purpose of positioning them, therefore it’s a layout.
This brings me a Container class that the Window now inherits from. Containers don’t seem to have a clear way of doing a hierarchy as each positions them differently. The two I have now, a Tetris like stack that I call Box (I might rename it to TetrisLayout) and TableLayout don’t even store their widgets in the same manner.
The UI code is really starting to look slick. It’s actually got more code than the current state of the graphics engine and more than 4x the code of any of the celestial mechanics and AI stuff I’ve already done.
I’m pretty sure I’m now 99% complete with the scrolling boxes. There were a couple of bugs; one which was interesting.
Because the box essentially draws its contents onto a render texture, the render texture created is the size of the screen. Then, as the box is scrolled, basically the region of this texture that’s then drawn to the boxes quad is updated.
What I found was that I was drawing the contents at normal window position on this texture. So a box close to the edge of the screen would result in the contents being partially drawn offscreen. Which is fine. Until you scroll and then the “region” of the texture you’re drawing is actually not on the texture. Graphical glitch.
Easy fix was to just draw to 0,0 on the texture and accommodate this in the region calculation. It also means that I can use a much smaller texture; probably the size of the widget contents. However, there would be a problem if that widget changed it’s size, I would then need to regenerate the texture, or resize it; and I’m concerned about the overhead needed. For now the screen dimensions cover all possible cases, so I’m using it until there’s performance problems.
I’ve also enabled mouse wheel and touch-pad scrolling via SDL2. So that’s pretty handy, makes it much more usable. I’m basically trying to recreate everything that people are used to.
It’s badly coded and needs some fixing up; but it works:
I’ve also been working on documentation of the UI system and need to complete that. It took far too long to complete the above because I had to keep going back over code to figure out what it was meant for.
It can never truly be 100% complete; but it’s pretty close. Cellpadding and cellspacing are in place as well as options to draw the borders. Elements are also positioned within their cell.
Now that I’ve got some way to position elements I can start working on text boxes; then the very tricky scrollbars.
I haven’t planned these out; but I think scrollbars need to handle a single…. Box, or Cell element. Every frame, the contents of the box or cell will need to be drawn to a texture, and then only the visible part of the texture is drawn to the destination; the scrollbar would adjust the region drawn. I can’t think of another way to hide the parts that are “offscreen”.
I spent the bulk of my available time this weekend writing the code for the Table Layout. It ended up quite a bit different from the design; chiefly because I found there was no need for an abstract row. The TableLayout class itself handles all the row…ing.
I just need to add some things like cellpadding, cellspacing, rowspan and colspan. Maybe some method to draw borders; though I might use a specialised DataTable class for that.