Archives

RSS Feed

Internal data for thinkers

My work has progressed to a point where it is becoming increasingly cumbersome that map objects remain POD C structs. While map data (sectors, lines, etc.) has long been refactored to be C++/PIMPL friendly for maximum flexibility, all variants of thinkers (including mobjs) remain inflexible and fully shared between the engine and the plugins.

There are many circumstances where thinkers would benefit from having internal data that is not exposed to external parties. For starters, it is a basic requirement for having ABI compatibility in the future: internal parts of the thinker state must be able to be refactored without causing all plugins to become incompatible. Also, the client has a lot of per-object state information for network games and rendering that would be much easier to deal with if it was directly owned by objects. In fact, the new GL2 lens flare code is very awkward to integrate to the renderer unless each object can also directly act as a light source — at the moment there is a cumbersome (and performance draining) mechanism of managing a second set of objects that describe the light sources cast by mobjs. The whole ordeal could be avoided if internally objects could own a light source.

Then there is the aspect of Doomsday Script integration. The appropriate way to make something a DS object is to have a Record that represents the instance. Such a Record needs to be mapped 1:1 to thinker instances, and the best way to do that is to have a Record inside the internal data of each thinker.

My current objective is to revise how 3D models are rendered, and a core piece of information for skeletal animation is the current animation state of an object: which animation sequences are running at which points in time in the model’s bones. Having this information somewhere outside an object would have a performance overhead in addition to being awkward to maintain. The internal data of a thinker could easily store the animation state, so that the engine is able to refresh it whenever needed (i.e., when the thinker thinks).

I’ve attempted one C++ refactoring of thinkers in the past, but it didn’t turn out well due to the scope of the changes being too large. Fortunately, the code base has long surpassed all the progress made in the new-order branches, so I’m feeling optimistic about attemping this again. This time, however, I will first tackle the absolute minimal step of enabling an opaque pointer in all thinkers for storing the internal C++ portion of the thinker.

I must be careful not to leak the C++ instance, though, as thinkers are zone-allocated and may disappear in a batch release.

When it comes to state persistency (savegames), I believe for now the internal data does not need storing, as we don’t yet have the DS Record and network/renderer state is rebuilt anyway when the thinker is restored. However, going forward, serializing a thinker will naturally need to include at least some portions of the internal data as well (the Record for starters).

There is at least one special mobj use case that needs special handling, though: sometimes game logic makes a backup copy of a mobj, and may then restore it after attempting some operation. The engine’s API must provide a special mechanism for this, since only the engine can take a backup of the internal data.