I once likened the current OpenGL backend to buying a new car just to drive to the store around the corner, only to ditch it afterwards. Ever since I wrote that OpenGL backend, I've been looking at making better use of that "car" once we had it.
My experiment with replacing all functions in simgraph16.cc with OpenGL calls was less than impressive. In order to get good performance with modern 3D hardware acceleration, simgraph must be bypassed completely, or at least for the most part.
Most of the stuff in Simutrans, be it grund_t or all ding_t that isn't vehikel_basis_t, is mostly static. If OpenGL geometry (VBOs) for these could be kept in VRAM and only updated when necessary, CPU and I/O load should be reduced. Unfortunately, Simutrans doesn't have a mechanism for informing the graphics system that a tile has changed. It can only set a flag on a tile marking it as dirty, and then the graphics system can detect it when looping through tiles to render them. I wanted a more up-front mechanism, which means changing karte_t a bit. I've also expanded karte_ansicht_t as the natural link between the map and OpenGL, making it the thing that's informed of changes from karte_t.
Currently, this experiment only renders a wireframe mesh of all grund_t. The OpenGL representation of the map is divided into a uniform grid, where each square cell is a fixed number of planquadrat_ts, except possibly to the east and south. Each cell has it's own VBO containing geometry for all grund_t within that square. When a tile is changed, only the cell containing it is marked as dirty. Before rendering, all dirty cells are recreated and the dirty flag cleared. Major map changing operations, such as loading, enlargement and rotation, cause the entire grid to be destroyed and recreated.
In this experiment, rendering is done as normal with the OpenGL backend, which is then overlaid the true OpenGL geometry with 50% transparency. The geometry is in the same coordinate system as used in koord3d. The modelview matrix transforms this to pixel coordinates, which is then projected orthographically.
Things of note:
- As a hack, the show grid tool deactivate normal map drawing (and unintentionally clearing, which isn't as much of a problem when only using the small default map generated at start-up).
- There aren't any capability or error checking. To test this, your computer must support VBOs, or the program will crash.
- The cells are color coded so that I can see their boundaries.
- Primitives are not generated for cliffs, so the geometry has holes.
- Tiles don't share verticies. This is because I foresee that they need different texture coordinates. This also makes it easier to introduce primitives for cliffs. The two triangles forming a tile could share verticies, but then the primitive type would have to be quads, not triangles, which doesn't suit cliffs and slope corners.
- What I call cells here, is just called a batch inside the static_geometry class.
- I've used std::vector as this is experimental code, and I want to use classes I'm familiar with at the moment.
- I haven't applied the latest suggested coding style to this experiment, but at times tried to follow the surrounding coding style when modifying existing code.
Thoughts about the way forward:
- Currently, all cells are rendered. Need to figure out which cells are not visible at all.
- Ground textures in Simutrans are actually created from basic components when loading the pak set. It could be possible to just upload these basic components to VRAM and let the hardware do the compositing. This probably requires fragment shaders, but might reduce video memory footprint.
- Static things like ways, trees and buildings can probably be kept in the same VBO as the geometry. I haven't looked into how animations would work, or how often changing signals and junctions would cause a cells to become dirty.
- It's probably best to just stream moving stuff, like vehicles and pedestrians, to the GPU. Trying to cache geometry for vehicles standing still might be more trouble than it's worth.
- Use the z-buffer, rather than (some modification of) painter's algorithm, to merge static and dynamic stuff. Need to figure out how to determine an optimal z-range, because a map wide z-range might lead to ugly artifacts on large maps.
- I'm not sure whether to treat windows as static or dynamic, as full geometry, or just textured rectangles.