ValveSoftware / ValveSoftware/source-sdk-2013

VBSP generates wrong collision for some brushes

Open
#1,903 0 comments 15 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C++
Stars
10k
Forks
3k
Avg merge
8d 11h
Merged PRs (30d)
2

Description

https://github.com/user-attachments/assets/f1d71779-8400-446a-bdbf-93a9e2a9738f

trikz_kyoto_fix
setpos 13485.354492 -10322.815430 -12656.668945

Explanation

The issue lies with
https://github.com/ValveSoftware/source-sdk-2013/blob/3300848d8a25ef6403c91f82a4cd97d6daefbc06/src/utils/vbsp/map.cpp#L471
Once an edge bevel plane is calculated, it is checked for correctness.
https://github.com/ValveSoftware/source-sdk-2013/blob/3300848d8a25ef6403c91f82a4cd97d6daefbc06/src/utils/vbsp/map.cpp#L564-L606
Afterwards CMapFile::FindFloatPlane is used to insert it into the map (line 601), but the function only does so after trying to SnapPlane to an axial one.
https://github.com/ValveSoftware/source-sdk-2013/blob/3300848d8a25ef6403c91f82a4cd97d6daefbc06/src/utils/vbsp/map.cpp#L336-L349

Aside from invalidating the earlier checks, this rounding will make the bevel plane's normal identical to one of the six mandatory axial brush sides added/verified earlier in CMapFile::AddBrushBevels. But convex brushes simply can't have two sides with the same normal, so the result is an incorrect collision against AABB (the only thing that uses bevel planes).
Such bevels are generated for edges that nearly coincide with an axial plane. For me, this occurs when copy-pasting or moving a brush: a vertex originally aligned to the unit grid gets shifted slightly.

Fix

Removing the rounding will do the job, but for edges less than a hundred units long, such bevels aren't really noticeable. So you might want to ignore them entirely if the edge is short, as this would also help stay under the maximum plane count.

To fix collision on already compiled maps, one could create a plugin that, after a level change, iterates through the map's brushes and removes every axial brush side beyond the first six.

Update: wrote my own fix https://github.com/L0jk4/AxialBevelRemover

Extra

Map from the clip decompiled with bevels taken into account to reflect AABB collision:
Image

An exaggerated illustration of what's going on:
Image

A visualization with accurate calculations:
https://www.desmos.com/calculator/dtupjyjppy

Another little bug

https://github.com/user-attachments/assets/6a2d8be1-ca89-440a-8528-7b79fb2ac93e

If an edge's direction is close enough to one of the axes, then it doesn't get any bevels. Therefore, brushes with such edges suffer a loss in collision accuracy:
https://github.com/ValveSoftware/source-sdk-2013/blob/3300848d8a25ef6403c91f82a4cd97d6daefbc06/src/utils/vbsp/map.cpp#L546-557

Thanks to Maru for helping me compile vbsp and tweaking bspsrc.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/utils/vbsp/map.cpp, reading CMapFile::AddBrushBevels, FindFloatPlane, and SnapPlane around the linked lines. Reproduce the reported collision with the linked map position, then trace both bevel-plane rounding and the near-axis edge case. Done means the affected brushes produce correct collision geometry without introducing invalid or duplicate axial planes.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
game-dev, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.