Hierarchies and transforms

Ambient supports hierarchies of entities using the parent and children components. The user only specifies the parent component; the children are automatically derived from the existing parents. As an example, the following entities in the ECS

entity a:
entity b:
  - parent: a
entity c:
  - parent: a

will produce the hierarchy:

entity a
    entity b
    entity c

The entity::add_child and entity::remove_child functions can be used to add and remove children from a parent.

When using the model_from_url or prefab_from_url components, the entire model sub-tree will be spawned in, with the root of the sub-tree being added as a child to the entity with the component. Each entity in the sub-tree will be part of the hierarchy using their own parent and children components.

Transforms in hierarchies

Hierarchies are commonly used for transforms where a root entity is moved around and all its children should move with it. To apply transforms to a hierarchy, local_to_parent must be used:

entity a:
  - local_to_world: Mat4(..)
entity b:
  - parent: a
  - local_to_parent: Mat4(..)
  - local_to_world: Mat4(..)

In this case, b.local_to_world will be calculated as a.local_to_world * b.local_to_parent.

local_to_world and local_to_parent are the only matrices necessary here. However, it is often more convenient to work with translation, rotation and scale components:

entity a:
  - local_to_world: Mat4(..)
  - translation: vec3(5., 2., 9.)
  - rotation: quat(..)
  - scale: vec3(0.5, 0.5, 1.)
entity b:
  - parent: a
  - local_to_parent: Mat4(..)
  - local_to_world: Mat4(..)
  - translation: vec3(-2., 0., 0.)
  - rotation: quat(..)
  - scale: vec3(1., 2., 1.)

In this case, the local_to_world and local_to_parent will automatically be recalculated from translation, rotation and scale whenever they change; the following computations will happen in this order:


#![allow(unused)]
fn main() {
a.local_to_world = mat4_from(a.scale, a.rotation, a.translation);
b.local_to_parent = mat4_from(b.scale, b.rotation, b.translation);
b.local_to_world = a.local_to_world * b.local_to_parent;
}

Mesh transforms

The above will let you express any transform hierarchy, but to reduce the number of entities, you can also use mesh_to_local and mesh_to_world. When mesh_to_world exists, it replaces local_to_world as the “final” transform for the rendered mesh. It’s calculated as follows:

entity a:
  - local_to_world: Mat4(..)
  - mesh_to_local: Mat4(..)
  - mesh_to_world: Mat4(..)

#![allow(unused)]
fn main() {
mesh_to_world = local_to_world * mesh_to_local
}

This also means that you can attach a mesh in the middle of a hierarchy, with an offset. For instance, if you have a bone hierarchy on a character, you can attach an mesh to the upper arm bone, but without mesh_to_local/world it would be rendered at the center of the arm (inside the arm), so by using mesh_to_local/world you can offset it.

Opting out of automatically derived children

If you wish to manage the children component yourself, you can attach an unmanaged_children component to your entity. This stops children from being automatically populated, and it’s now up to you to populate the children component to create a valid hierarchy.