Portalized

Late last week I started running some of the older levels with the new camera angle. The crypt and poison levels had been running slow for quite a while, but I didn't really investigate.

When I ran the crypt through nvperfhud, I saw huge areas of the level being needlessly rendered that were in the view frustum, but occluded.

Bringing the camera closer to and behind the player had clearly changed the performance profile, and required some sort of better culling mechanism than view frustum and fog culling.

So, I decided to implement a cell & portal system to speed up some of these bad areas, and hopefully bring up the overall speed as well.

Now, AG doesn't use bsp's or heightfields or other geometry structures that lend themselves to easy culling - it just has a polygon soup that is broken into ~1000 polygon groups at level build time.

Years ago, I had invesitgated several approaches to automatic portal generation and occlusion culling, but in retrospect, it's so much easier if you have an easy, sloppy mechanism in the level editor to manually set up cells & portals.

The current scheme works like so :

Designers place oriented & scaled bounding boxes in the level to serve as cells ( or zones ). If two cells' boxes overlap, then those cells are considered neighbors and are always mutually visible.

If cells overlap with a portal between them, then they are not neighbors, and they can only see each other through one or more portals or a chain of mutual neighbors.

At runtime, the camera's cell or cells are found. If there is more than one cell ( possibly b/c of overlap ), they are each processed in turn. If no cell is found, portal culling is disabled for that frame.

If this cell has neighbors, they are added to the potentially visible list. If the cell has portals ( cell->portal->cell and neighbor relationships are determined at build time ), then if the portal is within the current frustum, a smaller frustum is constructed from the camera and the portal's viewspace extents, and then the portal's other cell is recursively processed with this smaller frustum.

When this process finishes, the engine has a set of potentially visible cells.

If a set of geometry, or an object's visual bounding box touches a cell, then the geometry is tested vs all of the cell's frusta to see if it touches those. If it touches any, in any of its cells, it's drawn, otherwise it's culled.

A cell can have multiple frusta if there are more than one portal from the same or nearby cells seeing into it.

After getting this working, the fps in one bad part of the crypt level went from 18 fps to 62 fps. Yes!

Today I will probably add some level editor features to make layout of cells & portals simpler, and add the anti-portals as well.

2 Comments

Recommended Comments

It's been a while, but I messed with PVS's and portal culling a while back. The bit I could never quite figure out was how to specify where the portals were. Your system sounds pretty intuitive and straight forward - think I might go kick myself.