Skip to content
McNopperPublic

About

Graphics file format optimized for agentic programming

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Aether

Build

The scene & material file format for the Hyperion and Theia renderers — a small C++23 library that parses TOML scene/material files and OBJ geometry into plain CPU data structures.

Aether — primordial deity of the bright upper air and pure light the gods breathe; the medium through which light travels. Aether is the medium: it carries a scene's description, untied to any renderer or GPU.

flowchart LR
    SM["slang-math<br/>math"] --> A["<b>Aether</b><br/>file format"]
    SM --> H
    A --> H["Harmonia<br/>shared Vulkan lib"]
    H --> Hy["Hyperion<br/>path tracer · ground truth"]
    H --> T["Theia<br/>real-time renderer"]
Loading

What Aether is (and isn't)

Aether is fully independent: it has no Vulkan, no GPU, and no renderer dependencies (only slang-math for math and toml++ for parsing). It turns text files into plain CPU structs; consumers (Harmonia and the renderers) handle all GPU upload.

Parses Into
<name>.scene.toml (TOML scene description) aether::SceneDesc (camera, render settings, env, tonemapper, post-tonemap renderer, geometry blocks with TRS + material refs, node graph with compounded TRS, material-library references)
<name>.materials.toml (TOML OpenPBR material library) aether::MaterialDesc (OpenPBR Surface v1.1.1 parameters + texture references)
Wavefront OBJ (geometry only — triangles only; OBJ material directives ignored) aether::MeshData / aether::MeshGroup (deduplicated vertices + indices, local space)

Geometry stays in OBJ (bulk vertex/index data); every other element — scene, camera, render settings, tonemapping, materials — is TOML, so files are small, single-purpose, and easy to edit (by hand or by an agent). TOML is the best compromise between human readability and token efficiency: compared to equivalent pretty-printed JSON, the TOML files save up to one third of the token usage when foreign files are read by an LLM/agent — less syntactic noise (no braces, fewer quotes), more content per token. Minified JSON would be denser still, but is no longer readable or editable by humans — and unlike JSON, TOML supports comments. Geometry is returned in object/local space; world placement lives on the scene's geometry blocks (TRS). Colors are kept in their declared input color space, tagged with MaterialColorSpace, so the consumer performs any color-space conversion.

Why TOML — and not OpenUSD or glTF?

OpenUSD and glTF are both excellent formats — but for different jobs than Aether's. glTF is a runtime delivery format: binary buffers optimized for engines to load, not for humans or agents to read and edit. Its 2.0 core material is metallic-roughness; OpenPBR is not yet fully supported — it is only partially reachable through material extensions (KHR_materials_*, still evolving) — and glTF 2.0 does not yet cover complex scenes. OpenUSD is a powerful composition and interchange system (layering, references, variants) but is heavyweight and complex — far more than a small hobby renderer needs, and not something you hand-edit in a text box.

Aether deliberately takes the best of both and stays small and agentic-friendly:

  • Human- and agent-readable/editable text (like neither format's binary payloads) — small, single-purpose TOML files with comments, cheap for an LLM to read and generate.
  • OBJ for bulk geometry — the one thing that genuinely needs a compact vertex/index container (glTF's buffers, but simpler and ubiquitous), kept separate from the text.
  • Lightweight composition — scene/camera/render/tonemap presets referenced and overridden (a small nod to USD layering) without USD's machinery.
  • OpenPBR verbatim — material parameters use the exact OpenPBR Surface 1.1.1 names, rather than glTF's or USD's own material models, so nothing is lost in translation.

The result: files small enough to diff and hand-edit, structured enough to compose, and faithful to OpenPBR — the sweet spot glTF and USD each sit to one side of.

File-format notes

  • Scene (<name>.scene.toml): top-level material_libraries array; [render], [camera], [tonemap] and [post_tonemap] tables; a [[mesh]] array of unique geometry declarations and an ordered [[instance]] array that places a mesh by name with a TRS and material. A [[mesh]] entry uses type = "box" (half_extents) or type = "sphere" (radius), or is omitted / "object" for an OBJ path; an optional bounds = { min=[...], max=[...] } table authors the object-space AABB (otherwise the renderer derives it from the vertices; generate it with tools/obj_bounds.py). Keys are spelled in full words for clarity (samples_per_pixel, environment_map, vertical_field_of_view, mesh, half_extents, material, …). The [render] table may select the scene-referred working color space via working_color_space = "lin_rec2020_scene" | "lin_rec709_scene" (absent → consumer default, Rec.2020); consumers convert all assets to it on load.

  • Node graph (optional [[node]] array): parent/child nodes that compound transforms. A node has a unique id, an optional parent (absent/empty = root), the same TRS keys as an instance (translate, rotate quaternion or rotate_x/rotate_y/rotate_z Euler, scale), and optional mesh + material/materials bindings — a node without a mesh is a pure transform grouping node. A node also carries optional agent metadata: description (prose: what this node/subtree is) and tags (categorization keywords) — pure AI context, never affecting the resolved instance list. The parser validates the graph (duplicate id, unknown parent, cycle are errors naming the offending id) and resolves it into the flat instance list: world = parent_world × local (T × R × S), one instance per meshed node, so several nodes may reference the same mesh (instanced subtrees). Flat [[instance]] blocks keep parsing exactly as before and stay first in the instance list; node-derived instances are appended after them — both may coexist in one scene.

    [[mesh]]
    name = "ball"
    type = "sphere"
    radius = 0.5
    
    [[node]]
    id = "arm"
    translate = [0.0, 1.0, 0.0]
    rotate_y = 30.0
    
    [[node]]
    id = "grip"
    parent = "arm"
    mesh = "ball"
    translate = [0.5, 0.0, 0.0]   # world placement compounds the arm's TRS
    material = "Ceramic"
  • Agent metadata (optional [meta] block): pure AI context — the renderer never reads it, and absent or wrong-typed keys parse leniently (never an error). Scene-level: schema, version, model, title, description, units, generator, plus assembly (prose: how the scene fits together — what the parts are and how they connect). Per linked file: [meta.link."<path>"] summaries (kind, description, bounds, triangles, materials) so an agent can understand the scene without opening the links. One compact example:

    [meta]
    title = "Shaderball rig"
    assembly = "The arm node parents the grip; the grip places the ball mesh."
    
    [[node]]
    id = "grip"
    parent = "arm"
    mesh = "ball"
    description = "Grip tip of the arm - this subtree is the end effector."
    tags = ["rig", "end-effector"]
  • Camera preset (<name>.camera.toml): a small standalone preset file with translate, rotation (rotate quaternion or rotate_x/rotate_y/rotate_z Euler), vertical_field_of_view and ev100 keys. It is meant to be referenced from the scene file's [camera] section.

  • Setting presets (presets/<name>.<group>.toml): the [render], [camera] and [tonemap] and [post_tonemap] sections may carry a reference = "presets/…" key pointing at a standalone preset file (top-level keys, same shape as the inline section). The referenced file is applied first as a base, then any inline keys override it (local wins). Settings shared across scenes therefore live in a single deduplicated preset file (e.g. all Cornell scenes share presets/cornell.camera.toml and presets/preview.render.toml). References resolve relative to the scene directory.

  • Materials (<name>.materials.toml): one TOML table per material (keyed by name), using OpenPBR Surface v1.1.1 parameter names verbatim; optional top-level colorspace and model (default "openpbr" — the material model tag, anticipating additional material models in the future). Texture bindings use Aether's map_* keys.

  • OBJ: geometry only. Non-triangle faces are skipped and all usemtl / mtllib directives are ignored — materials are assigned exclusively from the scene file.

Authoring tools

See tools/README.md for the Blender scene exporter and the MaterialX-to-TOML converter. The Blender addon exports a .scene.toml, a matching .camera.toml and OBJ files; OpenPBR materials stay in TOML and can be authored separately or generated from MaterialX OpenPBR libraries.

Tip: in VS Code, install the Even Better TOML extension for syntax highlighting, formatting, and (with a JSON Schema) live validation of the .toml files.


Building

Requirements: CMake 3.28+, a C++23 compiler, vcpkg (provides toml++; slang-math is pulled via CMake FetchContent).

cmake -S . -B build -G Ninja \
      -DCMAKE_BUILD_TYPE=Release \
      -DCMAKE_TOOLCHAIN_FILE="<vcpkg-root>/scripts/buildsystems/vcpkg.cmake"

cmake --build build

Tests

GoogleTest-based unit tests (pure CPU — no GPU required):

cd build && ctest --output-on-failure

Consuming Aether

Hyperion, Theia and Harmonia pull Aether via CMake FetchContent and link the aether::aether target.


Dependencies

Library Purpose
slang-math Vector / quaternion math (header-only, via FetchContent)
toml++ TOML parsing for the .scene.toml / .materials.toml formats
GoogleTest Unit testing (tests only)

References

The material model follows the OpenPBR Surface specification; MaterialX is the canonical node reference used by the tools/ MaterialX→TOML converter.

About

Graphics file format optimized for agentic programming

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages