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.