SCHEMAXIS · Declared work. Results you can check.Documentation ↗ · Contact
schemaxis/Agent’s Hands

Documentation / godot.md
Core 11629ae · View or edit Markdown on GitHub ↗

Godot scene adaptor

schemaxis.godot/1 constructs small editable 3D Godot projects from declared JSON. The host uses Python's standard library and Schemaxis's existing validation/fault modules; Godot runs in two separate headless processes, with an optional third graphical process for a PNG preview. No authored GDScript, native reflection, arbitrary properties, or shell commands are accepted.

API for integration#

Import from schemaxis.adaptors.godot or schemaxis.adaptors.godot.adaptor:

API Contract
DECLARATIONS Mapping of operation name to native, kinds, options with nested value declarations, and typed inputs.
plan(document) Pure, deterministic JSON-compatible plan, version schemaxis.godot.plan/1. Does not launch Godot or read geometry files. Defaults are expanded; input is not mutated.
schema() Closed JSON Schema for literal schemaxis.godot/1 documents. Cross-node and camera constraints are enforced by plan.
run(document, directory, executable=None, timeout=120) Builds and reopens in distinct Godot processes, verifies readback, returns a JSON-compatible verified result. Directory must not exist. Executable must be absolute, or set GODOT_EXECUTABLE. Timeout is a total budget across all native stages including optional preview, in seconds, bounded to (0, 3600].
verify(directory) Offline validation of retained input, receipts, digests, and native readback. Does not launch Godot again. Raises a structured fault for incomplete or modified evidence.

All domain faults are schemaxis.core.faults.SchemaxisError with a stable code, location and recovery guidance. Failed native runs retain result.json with verified: false, the structured fault and native logs. Preflight failures do not create a job. The returned successful result contains status, verified, artifacts (2), nodeCount, runtime, and absolute project and scene paths. With "preview": true, artifacts is 3 and preview is the absolute PNG path.

The adaptor registers its host-safe contracts in its own adaptor.json. Shared CLI, schema, console and reference entry points discover that manifest and delegate to these functions. Workflow scheduling, verified artifact handoffs and resume are supported. See media workflows for PNG sprite placement and artifact order. Geometry file dependencies are the options.file values on readGLTF nodes. Scheduling should use one serial native job; no MPI, simulation time series, general rendering or physics execution contract is provided.

Authoring#

The top-level keys are version, optional units (m only), nodes, and optional Boolean preview (default false). There must be 1–128 declarations, exactly one camera and at least one geometry or sprite node. Identifiers match [A-Za-z][A-Za-z0-9_]{0,63} and must be unique. Nodes use id, filter, optional options, and typed inputs.

Filter Options Inputs
transformGroup Local XYZ position, degree XYZ rotation, positive XYZ scale Optional parent group
material RGB colour, roughness, metallic, all channels in [0,1] None
box Positive XYZ size, XYZ position material
sphere Positive radius, XYZ position material
cylinder Positive radius, height, XYZ position material
plane Positive XZ size pair, XYZ position material
readGLTF Absolute .gltf file, XYZ position material
sprite3D Absolute PNG file, XYZ position, pixelSize in metres per pixel None
camera position, target, degree fov, near, far, Boolean orbit None
directionalLight Degree XYZ rotation, RGB colour, energy None

Material inputs must reference earlier material declarations; every declared material must be used so it is preserved and verified. Materials are native StandardMaterial3D resources, geometry is native MeshInstance3D, and all scene nodes are owned by the saved root. Primitive meshes retain their editable native parameters. Geometry has local position, rotation (degrees, Godot's YXZ Euler order) and scale; optional parent groups organise static assemblies (see below). Coordinates are metres, Y is up, and cameras look down -Z. Camera target must be separated from its position and not parallel to Y. Camera clipping requires near < far; visibility/framing is the author's choice.

Primitive dimensions are limited to 0.001–10000 m, and positions to ±100000 m. Sphere tessellation is fixed at 32 radial segments and 15 rings, which includes the equator; cylinders use 32 radial segments and one ring. schema() exposes all numerical limits and defaults.

See examples/schemaxis/godot-primitives.json for a runnable scene. examples/schemaxis/godot-gltf.json is a template: replace its absolute file path.

Geometry import and loss limits#

The deliberately narrow glTF 2.0 subset accepts one scene, one identity mesh node, one mesh, one TRIANGLES primitive, embedded base64 buffers, float32 POSITION and optional NORMAL attributes, and optional unsigned indices. Each resource is at most 8 MiB, all resources total at most 32 MiB, with at most three buffers/views/accessors and 100000 elements per accessor. External URIs, GLB, transforms, materials, textures, animation, skins, morph targets and extensions are rejected, not silently discarded. Geometry receives the declared Schemaxis material. The resulting native mesh is an editable ArrayMesh rather than a live link to the glTF file.

Input bytes are captured and hashed before native import. Bounds are computed independently from POSITION bytes, not trusted glTF extrema. Retained source bytes, native bounds and triangle count are checked offline. The original source may move or disappear after capture; verification uses the retained copy. This does not establish vertex-by-vertex topology, normal or visual equivalence.

Native output and verification#

run writes project.godot, then uses Godot's PackedScene.pack and ResourceSaver.save to create scene.tscn. A second process loads and instantiates the saved scene with the resource cache ignored. Both receipts record runtime, process id, node count, node ownership, geometry bounds and primitive parameters/tessellation, material values, light values and camera pose, projection settings and orbit state. Verification compares the receipts with each other and with the declared input. It checks SHA-256 digests of the project, scene, trusted scripts and retained glTF files.

Readback uses relative and absolute tolerances of 1e-5 for Godot single-precision values; Godot can give flat imported mesh AABBs approximately 1e-5 m thickness. Receipts and hashes detect accidental changes; they are not signed attestations against an actor able to rewrite all evidence.

Keep the output directory together. orbit.gd is a fixed trusted generated asset, not authored code. Open project.godot in Godot 4 and run the scene: left-drag orbits, the wheel zooms, and R resets the camera. orbit: false produces a fixed camera without an attached script. The remaining job JSON, logs and worker.gd are retained for audit. Editing the saved scene is supported in Godot, but intentionally invalidates the original verification receipt.

Qualification#

PYTHONPATH=src GODOT_EXECUTABLE=/absolute/path/to/godot \
  python3 -m unittest discover -s tests/unit -p test_schemaxis_godot.py -v

PYTHONPATH=src python3 tools/qualify_godot.py \
  --executable /absolute/path/to/godot \
  --directory /absolute/path/to/fresh-evidence --timeout 60

The qualifier constructs/reopens all four primitives, an independently measured indexed glTF triangle and a fixed-camera variant. It checks rejection of altered bounds, node count, material, camera and process evidence, and launches the saved project's actual main scene for three headless frames. Native checks use small CPU-only scenes. They do not establish visual quality, GUI editing or mouse interaction. Godot 4.7.2 was used for local native qualification.

Optional rendered console image#

Set "preview": true on a complete scene document. This adds one fixed 640×480 preview.png to the artifact hash manifest and result. The existing console manifest fallback can serve it without changes to shared console code:

set document preview true
preview
image

The preview is rendered from the saved scene in an isolated Xvfb display using Godot's actual X11/OpenGL compatibility renderer, Mesa software rendering and two llvmpipe threads. It requires Linux xvfb-run (including its X server/auth utilities), Mesa/OpenGL and an X11-capable Godot build. Missing xvfb-run raises PREVIEW_UNAVAILABLE; native startup/render failures remain structured faults with render.log. No preview silently substitutes a blank headless image.

The worker waits for three completed render frames, captures the viewport, saves PNG, reloads it natively and compares every decoded pixel. It records the real display/renderer, process id, scene readback and PNG digest in rendered.json. Host verification checks PNG dimensions, chunk CRCs, bounded decompression, scanline lengths/filter bytes and the artifact digest. A valid capture may be visually empty if the authored camera misses the geometry; successful capture is not a visibility assertion. The render process and its Xvfb wrapper share the job timeout and dispatcher cancellation context. Normal runs still require no graphical display.

Build, reopen and render all use schemaxis.dispatch.SubprocessRunner with schemaxis.core.runners.Interpreter. A fixed host-Python replay uses os.execvpe to become Godot (or xvfb-run), preserving the PID/session tracked by the dispatcher. Authored values are never executed as source. Inherited allocation/cancel state is not replaced. Cancellation and timeout retain the shared RUNNER_CANCELLED and RUNNER_TIMED_OUT fault codes and an unverified failed result. Each stage receives only the remaining monotonic deadline budget; cleanup may additionally take the dispatcher's termination grace period. No next stage launches after the budget expires. Replay sources, full logs and metrics are under .dispatch/<stage>/; <stage>-submission.json records the native argv as well as the host replay command. <stage>.log remains available for convenience.

Add --preview to tools/qualify_godot.py to exercise this path. Set GODOT_GRAPHICAL_TESTS=1 alongside GODOT_EXECUTABLE to run the graphical unit test, which checks that changing the material changes the actual PNG pixels. Local graphical qualification used Godot 4.7.2 and Mesa llvmpipe. The image was also visually inspected; GUI editing and mouse interaction were not qualified.

The capture follows Godot's documented viewport post-draw screenshot API.

Official API references consulted:

Development admission#

This adaptor now provides the development kit delivery contract, operation selection guidance, explicit optional-feature decisions and an editable acceptance example. Run the common host/native conformance recipe and inspect its scoped source-bound receipt; structural admission alone is not native qualification. The current audit records native availability and exact qualification limits.

Static assemblies#

Use transformGroup to name a pivot and move a static geometry assembly together. It creates a non-renderable native Node3D. Its optional position (XYZ metres, zero by default), rotation (XYZ degrees, zero by default) and scale (positive XYZ factors, one by default) define its transform relative to its parent. Every geometry operation and transformGroup accepts optional inputs.parent:

{"id":"arm","filter":"transformGroup",
 "options":{"position":[1,0,0],"rotation":[0,0,45],"scale":[1,3,1]},
 "inputs":{"parent":"assembly"}}

Omit parent to attach to the saved scene root. Parent ids may refer forward; the planner orders dependencies deterministically and rejects missing groups, non-group parents and cycles. Materials still reference earlier declarations. Flat documents retain their original order and meaning. Cameras, lights and sprites remain at the root; camera targets and orbit controls keep their existing world-space interpretation. This slice does not add animation, physics, scene instancing or Godot's unrelated named membership groups.

Transforms use Godot's intrinsic YXZ Euler convention: apply local scale, then Z, X and Y rotations, then translation; apply each ancestor transform afterward. Non-uniform ancestor scales with rotated descendants can produce shear. Both execution and the independent host verifier preserve the complete affine basis; neither approximates it by adding Euler angles or multiplying axis scales. Individual scales must be in [0.001,1000]; negative/mirrored and zero scales are unsupported. Hierarchies allow at most 16 parent links, world-position components within ±1000000 m, and world basis-column lengths within [0.000001,10000]. DEPENDENCY_CYCLE, INVALID_DEPENDENCY, TYPE_MISMATCH and RESOURCE_LIMIT identify the affected parent field; remove a cyclic link, use a group id, or reduce the nesting/accumulated transform respectively.

The fresh native process recursively measures every saved node. It records its actual parent, scene-root owner, local and global basis, global position and transform inheritance. Geometry bounds are world AABBs formed by transforming the original mesh-local AABB corners. Group bounds are the union of descendant geometry world AABBs; an empty group has null bounds. These are bounding boxes, not precise oriented surface envelopes or collision geometry. All descendants are owned by the root after add_child, so PackedScene.pack saves them even when their immediate parent is another group. Native readback is checked against host affine composition, not only against the first native receipt. The existing 1e-5 absolute and relative single-precision tolerances apply.

The editable nested assembly example uses forward parents and nested rotations/non-uniform scale. Studio's Static transform assembly starter adds a pivot and two locally positioned blocks. The bounded qualifier below checks independently derived literal world values, a changed ancestor transform, empty/aggregate group bounds, the legacy flat scene, ten jointly forged receipt variants and live timeout/cancellation retention:

PYTHONPATH=src python3 tools/qualify_godot_hierarchy.py \
  --executable /absolute/path/to/godot --directory evidence/godot-hierarchy

This slice was freshly exercised with official Godot 4.7.2 on 17 October 2026. Graphical interaction, animation and physics are outside this acceptance scope. The completion review separates these new checks from prior flat-scene and preview qualification. Official Node3D transforms, Node ownership and PackedScene packing were reviewed for the implementation.