srctools.geometry

In VMF files, brushes are defined solely by the direction of each face, and do not explicitly specify vertices. This makes manipulation and checks against them difficult. This module calculates the vertices of a brush, and provides the logic for operations like clipping and carving.

See utils/common/polylib.cpp for some of the algorithms.

Conversion

First, call from_brush to convert a Solid into a calculated geometry object.

classmethod Geometry.from_brush(brush: Solid) Geometry

Convert a VMF brush into a set of polygons with vertices computed.

These are composed of Polygon objects, which each wrap a Side object. After performing operations, call Geometry.rebuild to remake the Solid objects, reusing faces if possible.

Geometry.rebuild(vmf: VMF, mat: str) Solid

Rebuild faces and the brush for this geometry.

Parameters:
  • vmf – The VMF faces will be added to.

  • mat – If faces were newly created, assign this material to the created face.

Alternatively, polygons can be converted to SMD triangles for preview.

for triangle in Polygon.to_smd_tris(
links: list[tuple[Bone, float]],
) Iterator[Triangle]

Convert to SMD triangles. UVs are not fully correct yet.

Parameters:

links – The bone weights to use.

Operations

classmethod Geometry.carve(
target: Iterable[Geometry],
subtract: Geometry,
) list[Geometry]

Carve a set of brushes by another.

Parameters:
  • target – Brushes to carve.

  • subtract – Brush to cut into the others. If you want multiple, call carve() again.

Returns:

Result brushes. Brushes are omitted if fully carved, or may have been split.

Geometry.clip(
plane: Plane,
) tuple[Geometry | None, Geometry | None]

Clip this geometry by the specified plane, creating a valid brush.

If polygons are used in both brushes, they will be copied.

Parameters:

plane – The plane to clip along.

Returns:

(front, back) tuple. The two brushes are self and None if entirely on one side, otherwise this copies faces and returns two solids.

Geometry.merge(other: Geometry) Geometry | None

Merge two brushes together, undoing a clip operation.

This locates faces which fully overlap each other, then removes them, checking the result would remain convex. If no result is found, returns None. If a result is returned, the original faces are shared.

Coplanar faces in the first brush are prioritised over the second brush.

API

class srctools.geometry.Geometry(polys: list[Polygon])

A group of faces with vertices calculated for geometry operations.

polys: list[Polygon]

Faces making up this brush solid.

classmethod raw_carve(
target: Iterable[Geometry],
subtract: Geometry,
) list[Geometry]

Carve a set of brushes by another, without modifying faces.

New polygons will have their face set to None, and duplicated polygons will share faces. This is not valid, but allows post-processing to track sides precisely. The non-raw version calls unshare_faces() afterwards.

Parameters:
  • target – Brushes to carve.

  • subtract – Brush to cut into the others. If you want multiple, call raw_carve() again.

Returns:

Result brushes. Brushes are omitted if fully carved, or may have been split.

raw_clip(
plane: Plane,
) tuple[Geometry | None, Geometry | None]

Clip this geometry by the specified plane, without modifying faces.

New polygons will have their face set to None, and duplicated polygons will share faces. This is not valid, but allows post-processing to track sides precisely. The non-raw version calls unshare_faces() afterwards.

Parameters:

plane – The plane to clip along.

Returns:

(front, back) tuple. The two brushes are self and None if entirely on one side, otherwise this copies faces and returns two solids.

classmethod unshare_faces(
geo: Iterable[Geometry],
) dict[int, list[int]]

If faces are reused, duplicate the VMF side to make each unique.

This makes geometry cut by raw_clip()/raw_carve() valid to use again.

Returns:

Mapping from side IDs to any copies made.

class srctools.geometry.Polygon(
original: Side | None,
vertices: list[FrozenVec],
plane: Plane,
)

A face, including the associated vertices.

original: Side | None

The brush side this was constructed from, or None if this was created fresh.

vertices: list[FrozenVec]

Vertex loop around this polygon. The start point is not specified.

plane: Plane

The plane this face is pointing along.

build_face(vmf: VMF, mat: str) Side

Apply the polygon to the face. If the face is not present, create it.

Returns the face, since it is known to exist.