Mapping & Hammer

Areaportals, Hints and Occluders: Which One Solves What

Areaportals, hint/skip brushes and occluders compared: how each tool works, what it costs, when to place it and how to verify the result in game.

Hint brushes, areaportals and occluders solve three different visibility problems. A hint changes how visleafs are cut at compile time and costs nothing in game. An areaportal clips the view to an opening at runtime or shuts off the area behind it entirely. At runtime, an occluder hides only models that lie completely behind a surface.

Three tools, two points in time

VBSP cuts the interior into visleafs, and VVIS calculates which other leafs could be visible from each one (the PVS). The basics are covered in optimizing GMod map performance and VBSP, VVIS and VRAD explained. The three tools step in at different places:

  • Hint and skip act during the compile. You decide where VBSP cuts leafs, which changes the result of VVIS.
  • Areaportals divide the map into areas during the compile, but only decide in game what gets drawn.
  • Occluders work exclusively in game.

VVIS does not take areaportals into account: in our test map, two rooms still had each other in their PVS despite correctly placed portals. So in game the engine takes the PVS first, clips it at the areaportals and finally tests models against occluders.

Hint and skip: controlling the leaf cuts

A hint brush is a world brush whose cutting face uses tools/toolshint while all other faces use tools/toolsskip. VBSP picks its split planes from the faces of brushes. In the VBSP code of the Source SDK 2013, skip faces are never chosen, and a plane that would cut through a hint face is avoided unless that plane is a hint itself.

The classic example is a corridor that turns at a right angle:

  1. Without a hint, VBSP cuts along one of the two inner walls. The result is two leafs that share a portal. From the end of one arm, the engine also draws the end of the other.
  2. Place a wedge-shaped brush in the corner. Its hint face cuts off the outer corner: it runs at 45 degrees from outer wall to outer wall and touches exactly the inner edge of the corner.
  3. The hint cuts a triangular piece off the corner, and each arm keeps a leaf of its own. The two arm leafs only touch at the inner edge and no longer share a portal.

In our test with GMod's compilers this raised the count from 2 leafs and 1 portal to 4 leafs and 3 portals, because VBSP split the corner triangle once more along an inner wall. The two arm ends no longer had each other in their PVS. Inside the corner itself you still see both arms.

Every hint cut creates leafs and portals that VVIS has to calculate. More on that in VVIS taking forever.

Areaportals: clipping and closing openings

A func_areaportal is a single brush with tools/toolsareaportal that fills an opening completely.

  • Open: The engine narrows the view to the cutout of the opening. Leafs outside it are skipped, and every entity in the area behind is tested against that cutout by its bounding box.
  • Closed: The area behind is not drawn at all unless another open portal leads into it.

The most important rule: the two sides of a portal must lie in different areas. Every connection between two areas therefore has to be closed by a portal or by world brushes. func_detail, displacements, brush entities and props do not seal. More on that in func_detail vs world brush.

For doors, enter the door's name under "Name of Linked Door" in the portal and set "Initial State" to match how the door starts. In the Source SDK 2013 code, func_door, func_door_rotating and prop_door_rotating open every func_areaportal that names them as its linked door as soon as the door starts opening, and only close it once the door is fully shut. According to the Valve wiki, make the portal brush thinner than the door so that the closed door is still drawn.

func_areaportalwindow closes by distance instead of by door. Between "Fade Start Distance" (default 128) and "Fade End Distance" (default 512) it fades in the brush entity entered under "Rendered Window" and then shuts off the area. It cannot be linked to a door: it has no field for one, and the door code in the SDK only looks for func_areaportal.

Finding an areaportal leak

If a portal touches the same area on both sides, VBSP reports it like this:

Processing areas...Wrote mymap.lin
Areaportal leak ! File: mymap.lin
Brush 88: areaportal brush doesn't touch two areas

VBSP does not abort at this point. In our test with GMod's compilers it still wrote the BSP and the portal file, and VVIS ran through without an error. The finished BSP, however, contained only a single area and no areaportal data, so both rooms lay in the same area. We did not test how the game handles such a map. Because nothing stops, the message is easy to miss. More on that in reading a compile log.

  1. Load the .lin file in Hammer via Map > Load Pointfile. In our test the line led from one side of the portal through the open connection to the other side.
  2. If you do not know which portal is meant, jump to the brush ID from the message with View > Go to Brush Number.
  3. Follow the line and look for the gap: a second passage without a portal, a wall made of func_detail or displacements, or a portal brush that does not fill the opening completely.

func_occluder: hiding models behind a surface

A func_occluder cuts no leafs, does not have to seal anything and can stand freely in space. Every frame the engine checks whether a model's bounding box lies completely behind the occluding surface, and skips it if so. Only models are tested, meaning props, NPCs and players. An occluder does not hide world brushes or displacements, and the Source SDK 2013 client code leaves brush entities out of the test.

You build it as a brush inside something opaque, such as a wall made of func_detail, a hill made of displacements or a building made of props. The occluding faces get tools/toolsoccluder, all others tools/toolsnodraw. It only works from the direction in which you look at an occluder face.

According to the Valve wiki, occluders are more expensive per object than open areaportals. They are therefore only worth it when a large surface covers many models.

Comparison: which tool for which job

ToolHides world geometryHides propsCompile costRuntime costTypical use
Hint and skipyes, whole leafsyes, with the leafmore leafs and portals for VVISnonecorners in corridors and passages
Areaportalyes, outside the cutout, the whole area when closedyes, one by one via bounding boxlow, one extra leaf per portalfor every visible open portaldoors, windows, building entrances
Occludernoyes, if completely coverednonefor every occluder and every tested modelfree-standing view blockers in open terrain

This gives you the order: world brushes as view blockers first, then hints, then areaportals at chokepoints with a lot of content behind them, and occluders only where no wall can seal.

Checking in game

Test with a fully calculated VVIS. The following commands exist in GMod's engine. The first five are cheat-protected there and need sv_cheats 1, r_occlusion is not:

mat_wireframe 1
mat_leafvis 1
r_lockpvs 1
r_DrawPortals 1
r_portalsopenall 1
r_occlusion 0
  • mat_wireframe 1 draws everything that is rendered as wireframe, even behind walls.
  • mat_leafvis 1 shows the leaf the camera is in, and r_lockpvs 1 freezes the PVS so that you can fly around. Use these to check hints.
  • r_DrawPortals 1 outlines areaportals, and r_portalsopenall 1 forces all portals open. If the wireframe does not change when you toggle it, the portal saves nothing at that spot.
  • r_occlusion 0 switches the occlusion system off for comparison.

You can paste the compile log into the Map Scanner, which detects root causes and follow-up errors in VBSP, VVIS and VRAD logs.

FAQ

Can I control an areaportal with a trigger?

Yes. func_areaportal has the inputs Open, Close and Toggle. However, the portal has only one state for all players. For multiplayer the Valve wiki recommends always-open portals above all, then portals on doors, and trigger-controlled ones last.

How many areaportals are too many?

There is no fixed number. Every visible open portal costs processing time, and if it hides too little, the scene is faster without the portal. Every areaportal is also an entity and counts towards the edict limit.

Why does the room behind a door or window disappear?

Then a portal is closed although you can see through it. Typical causes are a portal with "Initial State" set to "Closed" that no door and no input ever opens, a linked door with a glass pane or a func_areaportalwindow whose "Fade End Distance" is too short.