File format¶
Plain JSON, version 3. Readable, diffable, and simple enough to write from another tool.
The envelope¶
{
"format": "am_landmark",
"version": 3,
"created": "2026-08-18T14:22:07",
"scene": "D:/show/asset/head.ma",
"landmarks": [ … ]
}
| Field | Meaning |
|---|---|
format |
Always am_landmark. |
version |
3 for files written by this build. |
created |
Local time the file was written, ISO 8601, seconds resolution. |
scene |
The scene it was saved from, if it had been saved. Informational. |
landmarks |
The list, in the order they were written. |
A landmark¶
{
"name": "jaw_line",
"type": "surfaceShader",
"color": [0.9, 0.35, 0.2],
"group": "head",
"notes": "under the ear, both sides",
"id": "9f2c1a7e4b8d4a1e9c3f5b2d7a6e0c14",
"shadingGroup": "jaw_lineSG",
"attributes": { "outTransparency": { "type": "float3", "value": [0, 0, 0] } },
"assignments": { "|char|headShape": [[0, 128], [204, 211]] },
"samples": { "|char|headShape": { … } }
}
| Field | Type | Meaning |
|---|---|---|
name |
string | The shader node name. |
type |
string | The Maya node type to create. Unknown types fall back to lambert. |
color |
[r, g, b] |
Display-space floats, 0..1, rounded to six places. |
group |
string | The group label. "" when unset. |
notes |
string | Free text. |
id |
string | A stable id, so a landmark can be recognised after renaming. |
shadingGroup |
string | The name the shading group had. Informational — the loader makes its own. |
attributes |
object | Curated look-dev extras; see below. |
assignments |
object | Mesh name → face ranges. |
samples |
object | Optional; mesh name → UV and/or position samples. |
assignments¶
Keys are the mesh names as they were in the source scene — full DAG paths where Maya gave one.
Values are inclusive face-id ranges: [[0, 128]] means faces 0 through 128,
129 faces in total. null means the whole mesh — an object-level assignment
rather than a per-face one.
samples¶
Optional. Written when Store UV samples or Store position samples was on.
"samples": {
"|char|headShape": {
"uvSet": "map1",
"uv": [[0.512, 0.771], [0.518, 0.774], …],
"space": "Object",
"point": [[1.02, 14.9, 3.51], [1.06, 14.87, 3.48], …]
}
}
| Field | Meaning |
|---|---|
uvSet |
The UV set the UVs were read from. |
uv |
One [u, v] per assigned face, in ascending face-id order. null for a face with no UVs. Rounded to six places. |
space |
Object or World, for the positions. |
point |
One [x, y, z] per assigned face, same order. Rounded to five places. |
The ordering is the contract: sample n belongs to the n-th face id in the sorted expansion of that mesh's ranges.
attributes¶
"attributes": {
"specularRoughness": { "type": "double", "value": 0.35 },
"transparency": { "type": "float3", "value": [0.1, 0.1, 0.1] }
}
Only a short whitelist per node type is written, and only plugs that are not
connected — a colour driven by a texture is skipped rather than flattened.
Types are Maya's own attribute types; double3 and float3 carry a
three-number list.
Reading one from somewhere else¶
The minimum a file needs to be loadable is a name and an assignment:
{
"format": "am_landmark",
"version": 3,
"landmarks": [
{ "name": "jaw", "color": [1, 0, 0],
"assignments": { "headShape": [[0, 5]] } }
]
}
Missing fields take sensible defaults: type becomes surfaceShader, group
and notes become empty, id is generated on creation.
Older versions¶
Every version of this format loads without conversion, so a file you saved a long time ago still opens.
| Version | How it differs |
|---|---|
| 3 | The current one. Adds samples, and with them the UV, Position and Colour Set match methods. |
| 2 | No samples, so such a file can only be loaded by Face Index. Everything else is the same. |
| 1 | {"version": 1, "materials": {...}} — a dict of materials keyed by name, each holding every settable attribute and the raw cmds.sets member strings. |
A v1 file's member strings are parsed back into ranges on load, and its colour is
recovered from whichever of color, baseColor, outColor or diffuseColor
it happened to record.
Files are always written at the current version. There is no "save as v2".
From Python¶
from Am_landmark.core import landmark_io
document = landmark_io.read_file("d:/head.json")
entries = landmark_io.parse_document(document) # v1, v2 or v3
for entry in entries:
print(entry["shader"], entry["color"], sorted(entry["assignments"]))
landmark_io.write_file("d:/copy.json", landmark_io.build_document(entries))
parse_document normalises every version into the same entry shape, so a script
that reads landmark files never has to know which version it was handed.