Axiom/SDK
SDK / Frame and camera

Frame and camera

When your code runs, where the camera is, how to turn a world position into a screen position, and what you are drawing onto.

frame@1

When your code runs, and how fresh what it reads is.

Answered by: the backend
on_frame(cb)

Fires after the game has built its frame and before present, which is the only point where reading world state and drawing over it agree with each other.

delta() -> f32

Seconds since the last presented frame.

index() -> u64

Monotonic frame counter. Useful as a cache key; the core uses it for the entity snapshot.

staleness() -> u32

Frames since the data you are reading was captured. Normally zero. Non-zero on backends that read a snapshot rather than live memory.

Returns
u32
0 means live
if (f.frame().staleness() > 0)
  // do not lead a target on stale data
camera@1

The view, as the game has it this frame.

Answered by: a pack
view() -> Result<Mat4>

View matrix, column-major, right-handed.

projection() -> Result<Mat4>

Projection matrix.

view_projection() -> Result<Mat4>

The product, when the pack can read it more cheaply than multiplying.

position() -> Result<Vec3>

Eye position in metres.

fov() -> Result<f32>

Vertical field of view in radians.

near_far() -> Result<(f32, f32)>

Clip planes.

project@1

The one most mods actually want. Runs host-side so matrix conventions never reach the mod.

Answered by: a pack
world_to_screen(p: Vec3) -> Result<Vec2>

A world point in screen pixels, DPI-scaled, top-left origin.

Errors
Failed
Off-screen or behind the camera
Denied
No pack answers camera; nothing involving screen position will work here
bulk world_to_screen_many(ps: [Vec3]) -> [Result<Vec2>]BULK

The bulk form. A radar projecting 300 positions should be one call; the scalar version in a loop is the single most common performance mistake in Tier W mods.

screen_to_world(p: Vec2, depth: f32) -> Result<Vec3>

The inverse, at a given depth.

visible(p: Vec3) -> bool

Inside the frustum. Cheaper than projecting and discarding.

world@1

Line of sight. Often unanswered, because it usually needs calling a game function rather than reading memory.

Answered by: a pack
trace(from: Vec3, to: Vec3) -> Result<TraceHit>

First solid thing along a ray.

Errors
Denied
Common. Degrade by drawing everything rather than refusing to draw.
visible_from(a: Vec3, b: Vec3) -> Result<bool>

Whether two points can see each other.

surface@1

What you are drawing onto.

Answered by: the backend
size() -> (u32, u32)

Backbuffer size in pixels.

dpi_scale() -> f32

Multiply authored sizes by this.

color_space() -> ColorSpace

srgb, linear, hdr10 or scrgb.

api() -> GraphicsApi

Informational only. It exists so a mod can log it or warn about a known issue, not so a mod can branch into API-specific drawing. A mod that branches on this to issue its own draw calls has left the portable surface.

HDR is a real trap

On an HDR swapchain a color of 1.0 is roughly paper-white reference level, not white, so naive UI comes out dim or searing. The overlay handles this for you; a mod using canvas should read color_space() only if it is doing its own tone mapping.