I designed the engine's level format and wrote a Blender script which creates levels from prepared .blend files. Levels use portals and bounding volume hierarchies for fast visibility detection. This way, both indoor and outdoor scenes can be rendered reasonably fast and, most importantly, mixed. For now, portals have to be set manually in Blender. It works like this:
- The artist creates an indoor level as a series of connected rooms. Every room must be a separate object.
- Portal polygons are created for every door, window, etc. which connects two rooms or a room with the outside. Every portal polygon must be kept as a separate object, its name must begin with "p_" to tell the export script it's a portal.
- The export script creates a list of rooms and portals and their connections. In addition, a bounding volume hierarchy is created from the rooms and free objects (landscapes, detail meshes, etc.)
This way, portals are merely an optional optimization; if the scene you're working with it rather simple or you simply want to preview a complex level and don't care to much about speed, you can simply skip portal creation; potential rooms will then be placed in the bounding volume hierarchy.
A portal's definition in the level file may look like this:
portal02 // portal's name room02 // target room's name 4 // number of vertices -2.947355 0.001296 -4.529554 // vertex #1 -2.947347 3.001296 -4.529551 // vertex #2 -4.947346 3.001301 -4.529550 // vertex #3 -4.947354 0.001301 -4.529554 // vertex #4
The next step was to put the new information to use. When a camera's render routine is called, it first determines the smallest sector it is in. Then it traverses the portals connected with this sector and recursively performs the following steps:
- If it is not completely visible, the portal is clipped to the screen.
- If the clipped portal has more or less than four vertices, it is replaced by its projected bounding rectangle.
- A new view frustum is computed from the clipped portal, using the portal's plane as the new near clipping plane.
- The sector is rendered using the new frustum.
- The entity components (meshes, sprites, etc.) contained in the sector are rendered.
- Sub-sectors are rendered.
Here's a screenshot with debug visualizations of the portals created during the recursive process:
The player's camera sees what is displayed in the top-right viewport. It is positioned in the first room. The doorway acts as a portal to the adjacent corridor, which in turn acts as a portal to the next room, and so on. This means that only parts of all rooms following the first are visible from the camera's current position, and only these parts and the entities they contain have to be rendered.The white frustum visualizes the camera's perspective from the first room, the turquoise frustum its perspective from the adjacent corridor, etc.