Veduta

Scene — assets/scenes/<name>.vscene

A scene is the starting state of a level: one camera, one directional light, a background color and an ordered list of entities. Each entity has a transform, an optional model and material, tags, an optional parent, and a kind that selects its Go behaviour.

File and name

Units and conventions

Top-level fields

FieldTypeDefaultMeaning
vedutastringrequiredMust be exactly "scene/1".
cameraobjectrequiredThe view the scene is rendered from. See Camera.
lightobjectsee LightThe directional light and ambient term. May be omitted entirely.
backgroundcolor"#202830"Clear color behind everything. An alpha byte is kept (it becomes the alpha of empty pixels).
entitiesarray of objects[]The entities, in order. Entity ids are assigned 1, 2, 3, … in this order when the scene is loaded; entities spawned later get the following ids.
mapstringnoneA tile map (docs/map.md) drawn under the entities: assets/maps/<map>.vmap.

Camera

FieldTypeDefaultMeaning
typestring"perspective"perspective or orthographic.
fov_degnumber60Vertical field of view in degrees, strictly between 1 and 179. Perspective only: an error on an orthographic camera. The horizontal extent follows from the output aspect ratio.
sizenumbernoneVisible height in meters (> 0). Required for orthographic, an error on perspective. The visible width is size × aspect ratio.
nearnumber0.1Near clip distance in meters, > 0.
farnumber200Far clip distance in meters, > near.
positionvectorrequiredEye position.
look_atvectorrequiredPoint the camera looks at; must differ from position.

The camera's up direction is +Y. When the camera looks straight up or down (the view direction is within about 0.8° of the Y axis), up becomes −Z instead, so the top of the image points toward −Z (north on a top-down view).

A 2D game uses an orthographic camera looking down −Z, sprites as thin quads, hitbox and layer: the 2d docs topic is the recipe.

Light

FieldTypeDefaultMeaning
directionvector[-0.4, -1, -0.3]Direction the light travels (from the light toward the scene), world space. Any non-zero length; the renderer normalizes it. [0, -1, 0] is light straight from above.
colorcolor"#ffffff"Light color, #RRGGBB only (no alpha).
ambientcolor"#404040"Ambient color added to every lit surface, #RRGGBB only.

Shading is Lambert, evaluated per vertex: light = ambient + color × max(0, n · −d̂) where n is the vertex normal and d̂ the normalized direction; the surface color is albedo × light × texel, each channel clamped to [0, 1]. Materials with "unlit": true ignore the light (color = albedo × texel). When light is omitted all three defaults apply; inside light, each field is optional.

Entities

FieldTypeDefaultMeaning
namestringrequiredUnique within the scene. Scenarios, traces and parent refer to entities by name.
kindstringrequiredBehaviour selector. Built-in kinds: static (no behaviour: scenery), camera, light. Any other valid name must be registered by the game in Go with veduta.RegisterKind; the scene compiler accepts it, and an unregistered kind is reported when the game loads the scene.
modelstringnoneName of a model asset (assets/models/<name>.vmodel). Without a model the entity is invisible in renders but still exists (triggers, spawn points, logic).
materialstringnoneName of a material asset (assets/materials/<name>.vmat). It is used by the model parts that do not name a material of their own; parts with their own material keep it. Parts left without any material use the default material (white, opaque, lit).
positionvector[0, 0, 0]Translation in meters.
rotation_degvector[0, 0, 0]Rotation in degrees (order above). Any finite value.
scalevector[1, 1, 1]Scale per axis. Every component must be non-zero; a negative component mirrors along that axis.
tagsarray of strings[]Labels used by game code, invariants (no_overlap:gem,wall) and inspection (entities tagged important must be on screen). Valid names, no duplicates within an entity.
parentstringnoneName of another entity of this scene. The entity's position, rotation_deg and scale are then relative to the parent (world = parent world × local, applied scale, then rotation, then translation). The parent may appear before or after the child in the list. An entity cannot be its own parent and parent chains must not form cycles.
visiblebooleantruefalse keeps the entity in the simulation but does not draw it.
hitbox[[minx, miny, minz], [maxx, maxy, maxz]]noneA box in the entity's local space (before its scale, rotation and translation) that replaces the model's bounds as the entity's AABB: collisions, ctx.Overlapping, no_overlap invariants and the trace's aabb all use it. Two vectors of finite numbers with min ≤ max on every axis. An entity without a model gets an AABB from its hitbox alone (a trigger zone). Use it to give a flat sprite some thickness, or to make a collision box smaller than the drawing.
frameinteger0The frame of the material's grid (a sprite sheet) the entity shows, 0 to 65535, left to right then top to bottom; without a grid it has no effect.
animstringnoneA clip of the entity's texture (docs/texture.md) it plays from the start: the engine sets frame every tick.
layerinteger0Draw order, in [-1000, 1000]: entities on lower layers are drawn first. Within a layer, opaque parts are drawn in id order, then blended parts back to front by the depth of their center along the camera's view axis. Opaque and cutout parts write depth, so among them the nearest surface is in front whatever the layer. Blended parts write no depth: the layer decides which blended surface covers which, and an opaque or cutout part on a higher layer is drawn over a blended part on a lower layer whatever their depth. A translucent overlay must therefore be nearer the camera than what it covers and on a layer at least as high.

model and material are references by name: the scene compiles even if the assets do not exist yet; inspect scene reports SCENE_MISSING_ASSET for missing ones.

Compiled form

The compiler produces asset.Scene: Name, Camera (Ortho, FovDeg — 0 for orthographic, Size — 0 for perspective, Near, Far, Position, LookAt), Light (Dir as written; Color and Ambient as linear RGB in [0, 1], each channel = byte / 255), Background (packed 0xAARRGGBB) and Entities in file order with every default filled in (Parent is the parent's name, Tags nil when empty, Hitbox a *gmath.AABB, nil when absent, Layer an int). The binary layout in .vda files is in docs/vda.md (chunk SCEN).

Errors (examples)

main.vscene:4:31: camera.look_at: must differ from camera.position {0 5 10}
main.vscene:9:62: entities[1].scale[1]: must be non-zero
main.vscene:12:45: entities[3].name: duplicate entity name "gem" (first used by entities[2])
main.vscene:14:20: entities[4].parent: parent cycle: arm -> hand -> arm
main.vscene:16:52: entities[5].hitbox[1][1]: max y (0) must be at least min y (0.5)

Full example

{
  "veduta": "scene/1",
  "camera": { "type": "perspective", "fov_deg": 60, "near": 0.1, "far": 200,
              "position": [0, 5, 10], "look_at": [0, 0, 0] },
  "light":  { "direction": [-0.4, -1, -0.3], "color": "#ffffff", "ambient": "#404040" },
  "background": "#202830",
  "entities": [
    { "name": "ground", "kind": "static", "model": "ground", "material": "grass" },
    { "name": "player", "kind": "player", "model": "hero", "material": "hero",
      "position": [0, 0, 0], "rotation_deg": [0, 180, 0], "tags": ["player", "important"] },
    { "name": "hat", "kind": "static", "model": "hat", "parent": "player",
      "position": [0, 1.8, 0], "scale": [1.1, 1, 1.1] },
    { "name": "gem_1", "kind": "collectible", "model": "gem", "material": "gem",
      "position": [3, 0.5, -2], "tags": ["gem"] },
    { "name": "exit", "kind": "static", "position": [0, 0, -9],
      "hitbox": [[-1, 0, -0.5], [1, 2, 0.5]], "tags": ["exit"] },
    { "name": "spawn_point", "kind": "static", "position": [0, 0, 8], "visible": false }
  ]
}

Orthographic top-down camera (only the camera shown; entities may be empty):

{
  "veduta": "scene/1",
  "camera": { "type": "orthographic", "size": 20, "position": [0, 30, 0], "look_at": [0, 0, 0] },
  "entities": []
}