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 common to use 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 renderered 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 created, and it’s now up to you to populate the children
component to create a valid hierarchy.