acron.geom

Procedural geometry — primitives for meshes generated by code.

Built for games that ship no mesh assets: every solid is emitted by these builders. The shapes follow one set of conventions, held by the package’s tests/test_geom.py:

  • boxes are centred on the origin;

  • cylinders and cones run along +Y, centred;

  • circles lie in the XY plane facing +Z;

  • a lathe spins a 2D profile (x = radius, y = height) about +Y.

Everything accumulates into one GeomBuilder, which emits a single Acron GeomNode with position, normal and colour — one draw call for a whole sub-assembly, which matters when a vehicle is a hundred-odd parts.

GeomBuilder([uv])

Accumulates triangles, then bakes them into one GeomNode.

Global Functions

color4(hex_rgb: int, alpha: float = 1.0) → tuple

0xRRGGBB, the form every authored colour is written in, converted to linear.

Everything downstream of the object shader is linear radiance — the tone map lives in the final pass — so a colour written as sRGB has to be de-gammad here or every surface comes out washed out.

flat_normals(pos) → list[tuple[float, float, float]]

Flat shading on non-indexed geometry: one face normal shared by the triangle’s three vertices (pos is a sequence of (x, y, z), three per triangle).

icosphere_positions(radius: float = 1.0, detail: int = 0) → list[tuple[float, float, float]]

Subdivided-icosahedron vertex positions, as a list of (x, y, z) rounded to float32.

Non-indexed: every triangle carries its own three vertices, which is what lets a fracture surface shade flat. And float32 — GPU precision, rounded HERE because a displacement hook reads the positions back out of the buffer, so the rounding must happen before the noise is sampled and quantised into facets.

xform(pos=(0, 0, 0), rotate=None, scale=None) → LMatrix4f

Compose a transform: scale, then rotation, then translation.

rotate is (axis, radians), or a list of them applied in order — the build code rotates about explicit axes far more often than it uses Euler angles.