CPU Rasters

The acron.raster module holds the pixel work a game does on the CPU and that has no business in an interpreted loop: painting a HUD instrument, baking a noise field, resampling a panorama. It is plain C++ over engine types (Texture, PfmFile, PNMImage, PTA_float), so a C++-only game uses it as it is, and the Python tier (acron.hud, acron.noise, acron.hdri) is a thin layer over it: see Menus, HUDs and Text, Procedural Content and Materials and PBR.

RasterPainter

An anti-aliased 2D painter over an RGBA8 store, for the drawing RmlUi cannot do: a minimap, a compass tape, a dial, a scope trace. Coordinates are a 2D canvas’s (y down, texel centres at +0.5, angles from +x toward +y); fill_rect, line, lines, polyline, polygon, disc, arc and blit compute their coverage analytically over their own bounding box and composite “over” once (lines and polyline take the union of their segments, so joints do not darken). The store is kept in the texture’s own layout – BGRA, bottom row first, premultiplied – so upload() is a memcpy into a Texture RAM image, skipped when nothing was drawn. Draw the card with TransparencyAttrib.M_premultiplied_alpha.

ValueNoise

Value noise on exact 32-bit integer hashing: hash2i, vnoise, fbm, ridged and the periodic pvn, and make_field / make_field_float to evaluate one of them on a regular grid (a 2048-square six-octave fbm in about a tenth of a second). The results are bit-identical across machines and to the Python scalar twin in acron.noise: the source is compiled with floating-point contraction off and without fast math, so no fused multiply-add rounds differently, and it uses only IEEE-exact operations (no sin or exp from the C library). fbm and ridged sum at most 64 octaves, and none gives 0; a coordinate that is not finite samples 0.

PanoramaRemap

equirect_to_cube_face resamples an equirectangular PfmFile onto one face of a cube map (bilinear or nearest, any channel count), in the face order and orientation TexturePool.load_cube_map uses; headless, so an asset build needs no GPU.

Painting

A painter is the size of the texture it fills. Colours are straight (not premultiplied) RGBA; the painter premultiplies as it composites. get_pixel reads a texel back as a colour and get_data the whole store as bytes, for tests. upload returns whether it wrote: an unchanged painter costs nothing, and mark_dirty forces the next upload. From Python the point lists of lines, polyline and polygon may be any flat sequence of floats (x0, y0, x1, y1, ...).

from acron.core import Texture, TransparencyAttrib
from acron.raster import RasterPainter

painter = RasterPainter(256, 128)
hud = Texture("hud")

def redraw(heading):
    painter.clear((0, 0, 0, 0))
    painter.fill_rect(8, 8, 240, 20, (0.1, 0.2, 0.3, 0.8))
    painter.polyline([10, 100, 60, 70, 110, 100], (1, 0.3, 0.2, 1), 1.5)
    painter.arc(128, 64, 30, (1, 1, 1, 1), 2.0, 0.0, heading)
    painter.upload(hud)

card.set_texture(hud)
card.set_transparency(TransparencyAttrib.M_premultiplied_alpha)

Noise fields

make_field evaluates one kind of noise – K_value, K_fbm, K_ridged or K_periodic (pvn with period) – at (x0 + i * dx, y0 + j * dy) for every column i and row j, and returns the values row by row, the first row at y0: doubles from make_field, floats (ready for a Texture RAM image or a heightfield) from make_field_float. The other arguments are the scalar functions’ own.

from acron.raster import ValueNoise

height = ValueNoise.make_field_float(ValueNoise.K_fbm, 512, 512,
                                     0.0, 0.0, 1 / 64, 1 / 64, 7, 5)
h00 = height[0]
assert abs(h00 - ValueNoise.fbm(0.0, 0.0, 5, 2.07, 0.5, 7)) < 1e-6

Panoramas

equirect_to_cube_face takes the face index (0 to 5, in the order TexturePool.load_cube_map reads # files) and the face size, and returns that face as a new PfmFile with the source’s channel count; cube_face_direction is the (unnormalised) direction a face texel looks along, for a caller that resamples by other rules.

from acron.core import PfmFile
from acron.raster import PanoramaRemap

sky = PfmFile()
sky.read("sky.hdr")
for face in range(6):
    PanoramaRemap.equirect_to_cube_face(sky, face, 512).write(
        f"sky_{face}.pfm")

make_field, make_field_float and equirect_to_cube_face are BLOCKING: they release the GIL, so a loading thread can bake while the game runs. Every result is deterministic.