As you might recall from older screenshots, my engine was only ever putting a single "room" on the screen - and consequently, only ever updating a single room of the game at a time. And while there are some (to my mind) compelling gameplay advantages to doing this, I'd always planned to make the engine able to handle large continuous worlds that allowed the character to travel smoothly from sprawling cities down into winding dungeons (besides, I've always fancied the idea of writing a pulp dungeon exploration game at some point).
Anyway, long story short (or at least shorter) I stumbled across a random D&D dungeon generator on the web and it got me thinking about how I could extend the engine to handle this.
Here's a simple example showing single room rendering:
Versus multi-room view of the same area:
The first thing I ruled off straight away was having a single continuous world space. The bigger a floating point number gets, the bigger its absolute error becomes. Depending on how sensitive your engine is to numerical precision, this will always place some kind of upper bound on how large across your levels/game can be. Given I use a lot of physics and (where possible) like to use numeric approximations for performance, my engine likes its numerical precision the same way I like my VISA bill: small. So a single worldspace is out.
The next option I was keen to explore was the one presented by Scott Bilas at GDC a few years back (and while I think of it - if you're technically minded and ever get the chance to go listen to this guy, I think he's brilliant). In this solution, each cell defines it's own local coordinate system, but at update/render time, the engine picks a "root" cell, and flood fills a temporary world space out to all the connected active cells. This creates a free-floating world space that only needs to handle precision issues out to the visible/active threshold, allowing the actually world to be (theoretically) limitless (and Dungeon Siege's continuous world is certainly a testament to this). To maintain precision, you either have to be very clever about the offsets between cells, or maintain two coordinates for each element (the high precision local transform, and the lower precision world one). Anyway, I was a fair way down this path (including a really cool scheme for allowing multiple free roaming observers to traverse the world merging and separating world spaces) when I realised there was something I wanted that this solution could never support. The problem with even a "local" worldspace is that, with a single coordinate system, every object exists in exactly one place. This means I could never do cool "impossible" things like connect the north door lead back into the east door, or build a mobius strip, or an infinite corridor, or flip gravity around and have a door open onto the floor in another room.
The option I finally went with was a generic "portal" system. In this system, every cell exists, updates and renders in it's own local coordinate system, and it defines the coordinate transforms for each connected cell. This allows me to create entirely impossible networks of rooms (I believe Valve's Portal does a similar thing). After much messing about, I finally got the whole visible scene rendering in a single render pass (e.g. allowing material sorting across cells, a single continuous shadow pass that allows one cell to cast a shadow through a portal into another cell, etc), and (while it's not always the best gameplay thing to do) you can now hook up infinite networks or rooms and run endlessly and smoothly in one direction, while the coordinate system flips and turns beneath you to change the orientation of the rooms you encounter.
Here's an infinite grid made up of 3 rooms (allowing our less than observant cow to shoot himself in the back):
Here are two doors that open back into the same room in different places (notice the changes in orientation, and hence projectile velocity, as you pass through the doors):
And zooming out, you can witness the awesome power of this fully operational ... umm, here's a zoom out shot:
You obviously wouldn't use these pathalogical circus tricks extensively in a real game, but they're certainly fun to experiment with and will hopefully open up some intriguing gameplay scenarios when used a little more subtly.
All of this is keyed off the camera. You can tell one camera to just look at a single room, while another has a bird's eye view of the whole network of rooms. There's also a mode for "real" portal rendering (where you can only see what's visible through the portals) but at this I've only got the culling working and still need to add support for pixel-perfect clipping. You might also notice I've cunningly avoiding creating any walls on the rooms in these screenshots - the other big todo is to add obscuring wall removal (so you can tunnel through the foreground objects and see into the room the character is in).
It worked out pretty well in the end: the object editor I wrote ages ago payed off yet again give me a really nice way to setup even complicated relationships between rooms and portals in the editor, and it feels really smooth storming through a network of rooms. As usual, comments are most welcome!
[small edit: I just realised all the screenshots here are offset out of the frame; looks like OSX windowing code has changed a little with the move to Intel - sorry about that!]