GUI: First Prototype

After some more work on the GUI system, I decided to capture a little video. Unfortunatley the mouse cursor wasn’t captured, so it may look a bit weird.

Up to now I have implemented Window, Text and Button elements. Windows can be resized and moved at will and may hold any number of the other GUI elements. The Text element supports strings which are formatted with basic HTML syntax. The text can be left, right, top, bottom aligned or centered. Buttons have various attachment points for callbacks which can be either written in C/C++ or Python script.

 

Basic GUI work

After having worked a lot on the renderer lighting/shadow mapping part of the engine and having digged through tons of math in the process, I decided to pause my work in that area and instead do something different. So I did some bug hunting, improved OpenGL state handling and cleaned up a lot of code.

Another important thing I started to tackle is the GUI framwork which is overdue for quite some time now. In the process of implementing a nice and simple GUI, I’ll rework the scene graph aswell and integrate it into the GUI as a special 3D-Scene widget. Up to now I have started to code a window and text class. They both are derived from a base class called nx_ScreenElement. Here is a stripped down code snippet of that class:

class nx_ScreenElement
{
  protected:
    nxRect_t m_position;
    I32 m_zOrder;
    bool m_visible;

  public:
    nx_ScreenElement(const nxRect_t& position) : m_position(position), m_zOrder(0), m_visible(true) {}
    virtual ~nx_ScreenElement() {}
    virtual bool onMessage(const nx_Message& message) = 0;
    virtual void update(const F32 deltaTimeInSeconds) = 0;
    virtual void render(const F32 deltaTimeInSeconds) = 0;
    virtual void restore() = 0;

    I32 getZOrder() const { return m_zOrder; }
    void setZOrder(const I32 zOrder) { m_zOrder = zOrder; }

    bool isVisible() const { return m_visible; }
    void setVisible(const bool visible) { m_visible = visible; }

    bool operator<(const nx_ScreenElement& rhs) const { return getZOrder() < rhs.getZOrder(); }
};

 

Every element on the screen will be based on this class. For example: nx_Scene, nx_GUIWindow, nx_GUIText, nx_GUIButton, … There is a master list of all screen elements in the engine which is sorted by z-order. Than system messages like KeyDown, MouseMove, MouseClick, … are send to the elements from front to back via the onMessage method. Each screen element may do some work based on a message and consume it or not. This way it is incredible easy to pass input to all active screen elements. Basically the same happens with update, render and restore. The render method for example is called on all screen elements in the list in reverese order to draw everything from back to front.

I’m still in the early prototyping and testing phase, but the first results are very promising. Here is a screenshot of 4 test windows which could be freely moved and resized on the screen. They are already correctly sorted by z-order and will switch to the front when selected. All colors are still static at the moment, but I have coded the classes in a way that it’ll be extremely easy to make it completely costumizable in the future. There is basic code for texturing/theming support in place aswell, even if I haven’t used it yet.

Shadow Mapping: Point Lights

Success! After having implemented shadow mapping for directional and spot lights, the engine now supports point lights aswell. As usual the code is a bit quick’n dirty and needs some serious cleanup. Nevertheless I’m already very pleased with the perfomance in the unoptimized state. The following video was captured with 4 fully dynamic, shadow casting point lights. FPS was around 70 on my pretty low tech PC: AMD Athlon X2 4450e (2,3Ghz Dual-Core), ATI HD4670. Even CPU usage was very low, so it’s definetly GPU bound. This leaves a lot of room for CPU stuff like game state handling, physics, culling, resource loading, sound and so on…

 

Shadow Mapping: Spot Light

I finished implementing shadow mapping for spot lights today. It’s only the first try, but it already looks pretty nice. I’m sure with some tweaks in the future it will be really good. Here is a screenshot of a single spot light. Blue line is the direction of the light and red lines are the lights view frustum. Bottom right is the shadow map used for the light.

Basic Shadow Mapping

Another step forward: Basic shadow mapping implementation is done. Up to now it’s only working for directional lights and still very hacky. It definetly needs a few optimizations and cleanups, but it’s still a nice start. I have tweaked my demo application a bit and added one directional light which is spinning around the center of the scene.

 

Deferred Shading: Demo

I build a little demo scene and captured a video. It has 4 fully dynamic point lights:

 

Furthermore the engine now supports directional, point and spot lights. All of them integrated into the scene graph and very easy to use. Next up are optimizations and shadow implementation.

Deferred Shading: First prototype

I finally did it! After several days testing, reasearching the net, going through tons of 3D math and uncounted frustrating hours, I have the first working prototype of the deferred renderer running. My main problems was a bug in my normals and mainly reconstructing the view space position from the depth buffer. Especially the on the last one was suprisingly tricky and forced me to dig through matrix math stuff again.

Nevertheless I have all main problems tackled and hopefully can do some nice things now. For the moment a screenshot of the prototype. It has 3 lights active and is fully deferred rendered.

Deferred Shading: G-Buffer

Another quick update on the deferred shading progress. I finished prototyping the G-Buffer. Here is the layout:

GL_R32F  |                       Depth                       |
GL_RG16F |         NormalX         |         NormalY         |
GL_RGBA8 |   ColorR   |   ColorG   |   ColorB   | MaterialID |
GL_RGBA8 | SpecularR  | SpecularG  | SpecularB  | Shininess  |

And now a screenshot. On the left you can see the 4 buffers, starting from the bottom: Depth, Color, Normal and Specular.

The main render is still done with default rendering. I’ll implement the deferred shading pass next.

 

NX-Engine progress

While still doing some Python integration I started working on several other parts of the engine.

1. Ported from SDL 1.2 to SFML 2.0

Up to now the engine used SDL 1.2 as it’s wrapper around low level stuff like creating windows and getting events. This worked ok so far but had some major drawbacks:

  • SDL is licensed under LGPL. While still an opensource license and far better than GPL, it is only allowed to dynamically link against the library.
  • SDL 1.2 isn’t able to create OpenGL 3+ contexts.
  • The source code isn’t as clean and readable as I would wish. (C code)

I was aware of SFML for quite a while and while the 2.0 version is still in development and has some bugs it offers clear advantages:

  • Lincense is zlib/png which is awesome because it allows dynamic/static linking + pretty much total freedom.
  • It is possible to create OpenGL 3+ contexts.
  • Source code is very nice and clean. (C++ object oriented)

So I got rid of SDL and plugged SFML into the engine. To my suprise this was easier than I thought and the whole process was smoothly done in a few hours. Maybe a sign for a good engine structure? I hope so!

While SFML has it’s benefits there was some drawbacks aswell. 1. Image loading support isn’t as good as SDL_image and 2. SFML doesn’t support condition variables for thread synchronization. I worked around this problems by integrating DevIL for image loading and porting all threading stuff to boost::thread.

DevIL is an excellent library with an absolutley lovely API which is pretty much the same as OpenGL. It offers support for an amazing range of picture formats and perfectly fits the needs of the engine.

2. gDEBugger fun and optimizations

If you haven’t heard of gDEBugger yet, it is an OpenGL profiler, analyzer, debugger with some nice features. It was recently made available for free and every OpenGL programmer should grab a copy immediately. With the help of this program I was able to optimize the renderer in the engine quite a bit by removing redundant GL calls. The results of 1 hour work were an increase of the FPS in valgrind’s memcheck by the factor 2,5x !

3. Actors for the engine

I started to implement some actor stuff in the engine. Up to know this allows the creation and deletion of objects through script. Next steps are moving things around by script commands and coupling of the system with the scene graph.

Python embedding (Part 2.5)

Last time I wrote about calling python functions from C/C++. I must admit I kept it very short, so today a bit more about this topic.

1. Wrapping PyObject* into a smart pointer

First I will show you how to use boost::intrusive_ptr to manage python objects. This frees us from doing ugly calls to Py_INCREF or Py_XDECREF all over our code and more important reduce the likelihood of introducing memory leaks by accident (python reference counting is really a mess in my opinion).

To use boost::intrusive_ptr you need to include the following file:

#include <boost/intrusive_ptr.hpp>

Next we need to add two additional functions to the boost namespace:

namespace boost
{
  inline void intrusive_ptr_add_ref(PyObject* p)
  {
    Py_XINCREF(p);
  }

  inline void intrusive_ptr_release(PyObject* p)
  {
    Py_XDECREF(p);
  }
};

They are used by the intrusive_ptr to do the reference counting for us.

Basically at this point we have everything we need, but I like to do one more thing to make my life a bit easier, a typedef for the intrusive_ptr:

typedef boost::intrusive_ptr<PyObject> nxPyObjectPtr_t;

This makes the code a lot cleaner.

2. Calling a python function by name

Now we have all tools needed to call a python function by name in a pretty safe way. This means keeping the probability of memory leaks to a minimum. Here is the method declaration which goes into the nx_PythonManager class:

nxPyObjectPtr_t callFunction(const char *name,
                             const nxPyObjectPtr_t& args = nxPyObjectPtr_t());

The first argument is the name of the python function you want to call. One thing to note here: The function must reside into the main python namespace (__main__ module). You can’t call functions from other python modules this way.

As you can see we are using our intrusive_ptr<PyObject> to pass arguments in and the return value out of the method. You’ll see in a second how exactly this works. If we wouldn’t have done it this way and returned raw pointers, the user of the engine would have needed to use Py_DECREF by themselves… not a good idea.

Now to the implementation:

nxPyObjectPtr_t nx_PythonManager::callFunction(const char *name,
                                               const nxPyObjectPtr_t& args)
{
  PyObject *main = PyImport_AddModule("__main__");
  PyObject *func = PyObject_GetAttrString(main, name);

  if (!func || !PyCallable_Check(func))
  {
    Py_XDECREF(func);
    return nxPyObjectPtr_t();
  }

This code should be familiar if you’ve read parts 1+2 of these article.  We just get the __main__ python module (in which our function needs to reside). Than we use PyObject_GetAttrString to get our function object. The next 4 lines are for error checking.

  PyObject *result = PyObject_CallFunctionObjArgs(func, args.get(), NULL);
  Py_DECREF(func);

Here is the actual call to the python function. The 2nd value passed is the argument list for the python function. Because we wrapped the PyObject* into intrusive_ptr we need to use “.get()” to get the raw pointer. The argument list can be anything you want: nothing, one python object or a complete python dictionary. The called python function is responsible for interpreting the list.

Last but not least we wrap the return value into an intrusive_ptr and return it:

  if (result == Py_None)
  {
    Py_DECREF(result);
    return nxPyObjectPtr_t();
  }
  else
  {
    return nxPyObjectPtr_t(result, false);
  }
}

Py_None is basically the same as “return void”, so we return an empty pointer.

One very important thing is the construction of the “nxPyObjectPtr_t”. You may have noticed the 2nd argument “false” this is extremely important. The intrusive_ptr contructor by default increases the reference count of the object: “intrusive_ptr(T *p, bool addref = true)”. Because we don’t want that, we need to explicity call it with “false”. If you’ll forget this you will leak memory. Because of the sneakiness of this behaviour I have created a macro which helps to “remember” the false:

#define NX_PY_OBJECT_PTR(pyObject) nxPyObjectPtr_t(pyObject, false)

Here is an example how to use it:

nxPyObjectPtr_t obj = nxPyObjectPtr_t(PyLong_FromInt(1000), false));
nxPyObjectPtr_t obj = NX_PY_OBJECT_PTR(PyLong_FromInt(1000));

The 1st line is without macro and the 2nd line with macro.

That’s all about it, wasn’t so bad after all.

3. Further reading

As usual some useful links about todays topics:

boost::intrusive_ptr

Smart pointers to boost your code (Tutorial)

Python reference counting

Next Page »