Gwen into our engine. Why Gwen? It is sleek, highly configurable, stable, well maintained, and close to production-ready out of the box. It does not throw exceptions and relies on simple data structures (if you consider STL as simple). It is quite well written and the code is easy to understand. And, yes, there is code to read. The library is open source and comes with an MIT license attached. Actually there are only two downsides: Gwen is not an immediate mode GUI and I would have loved to check that new paradigm out and Gwen uses dynamic_cast and thus requires RTTI. I’ll try to iron that out later on and will post my progress here. This time I’ll focus on how you hook up Gwen with your tech by telling you about how I hooked her up with *my* tech. There’s one more downside to Gwen: There’s hardly any documentation.
Finding Gwen
handy guide to hooking up your own renderer with Gwen. Out of the box, Gwen comes with OpenGL, DirectX9, GDI and SFML renderers. You can use those by instantiating the renderer and you should be fine. Gwen also includes input handlers for SDL, SFML and Windows. We’re using OpenGL and SFML for Ginkgo, so there was not too much work to do. Input handling worked out of the box. I just had to adapt the focus handling a little bit. See below for details. The rendering was also pretty much ready to use, but I wanted to hook up our own resource manager and font renderer rather than loading textures and fonts directly. For now, Gwen just renders in every frame after the game. Since we’re building a 2D engine, draw order equals depth and Gwen thus always stays on top of things.
Taming Gwen
In order to write a custom renderer for Gwen it is best to copy or inherit from one of the included renderers and just replace the functions that you need to. In my case, I had to provide the following functions:
virtual void Ginkgo::Begin() virtual void Ginkgo::End() virtual void LoadTexture( Gwen::Texture* pTexture ) = 0; virtual void FreeTexture( Gwen::Texture* pTexture ) = 0; virtual void LoadFont( Gwen::Font* pFont ) = 0; virtual void FreeFont( Gwen::Font* pFont ) = 0; virtual void RenderText( Gwen::Font* pFont, Gwen::Point pos, const Gwen::UnicodeString& text ) = 0; virtual Gwen::Point MeasureText( Gwen::Font* pFont, const Gwen::UnicodeString& text ) = 0;
Begin is called before any rendering takes place. Set up your projection and model view matrices as well as a pixel-exact viewport in this function. You can pop everything back to normal in the corresponding End() function. Load texture should load a texture via your resource manager. Fill out the struct Gwen::Texture and return it. You can save a pointer to your engine’s texture class in the data field of Gwen::Texture. Here’s how I implemented it:
void Ginkgo::LoadTexture( Gwen::Texture* pTexture ) { GIN::Texture *tex = GIN::TextureManager::instance().get(GIN::FixedSymbolize(pTexture->name.Get().c_str())); pTexture->width = tex->width(); pTexture->height = tex->height(); pTexture->data = tex; }
LoadFont does the same for fonts. RenderText and MeasureText both depend on your engine’s font rendering and should be implemented accordingly. We’re using a custom bitmap font renderer in Ginkgo, that can either write into pre-allocated interleaved arrays for per-vertex information and indices or return non-interleaved arrays for vertices, indices and texture coordinates. When I connect Gwen to our render manager (possibly the topic of one of my next posts) I’ll use the interleaved method. For now, I just have the font renderer allocate the data for me. Gwen is quite cleverly implemented. It caches rendering information and flushes the cache when it reaches a certain size and after it finished rendering. In order to properly fit your own rendering between Gwen’s cached vertex rendering you must Flush() before you render on your own.
One function that needs a little explanation is Translate(Gwen::Rect &rect), which is found all over the place in the renderers. Translate translates and scales the supplied rectangle and is the primary method for placing objects in Gwen. If you add your own rendering functions, be sure to Translate() all coordinates.
Make sure you load the default skin and at least one font before anything else. Set the appropriate values via SetDefaultFont and Init’s parameter of the skin. Here’s my setup function for the GwenManager. The texture “defaultSkin” and the font “editorFont” are both loaded in advanced.
GwenManager::init() { _gRenderer = new Gwen::Renderer::Ginkgo(); _gSkin = new Gwen::Skin::TexturedBase(); _gSkin->SetRender( _gRenderer ); _gSkin->Init(L"defaultSkin"); _gSkin->SetDefaultFont(L"editorFont"); V2f contextSize = RenderContext::instance().getDimensions(); _gCanvas = new Gwen::Controls::Canvas( _gSkin ); _gCanvas->SetSize( contextSize.x, contextSize.y ); _gCanvas->SetDrawBackground( false ); _gInput = new Gwen::Input::SFML(); _gInput->Initialize( _gCanvas ); }
With this information, and the tutorial, you should be able to integrate Gwen into your engine. I don’t regret choosing this GUI so far. Attaching events is a bit awkward, but I’ll come to that next time.