forked from bevyengine/bevy
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
transform: remove some dependencies and cleanup
- Loading branch information
Showing
11 changed files
with
14 additions
and
247 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
[package] | ||
name = "bevy_transform" | ||
version = "0.3.0" | ||
authors = ["Alec Thilenius <[email protected]>"] | ||
version = "0.1.0" | ||
authors = ["Carter Anderson <[email protected]>", "Alec Thilenius <[email protected]>"] | ||
edition = "2018" | ||
|
||
license = "MIT" | ||
|
@@ -11,10 +11,7 @@ bevy_ecs = { path = "../bevy_ecs"} | |
bevy_property = { path = "../bevy_property" } | ||
glam = "0.8.7" | ||
log = "0.4" | ||
rayon = "1.2" | ||
serde = { version = "1", features = ["derive"] } | ||
smallvec = { version = "1.4", features = ["serde"] } | ||
shrinkwraprs = "0.2" | ||
|
||
[dev-dependencies] | ||
env_logger = "0.7" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,211 +1,3 @@ | ||
# Hierarchical Legion Transform | ||
|
||
[![Build Status][build_img]][build_lnk] | ||
|
||
[build_img]: https://travis-ci.org/AThilenius/legion_transform.svg?branch=master | ||
[build_lnk]: https://travis-ci.org/AThilenius/legion_transform | ||
|
||
A hierarchical space transform system, implemented using [Legion | ||
ECS](https://github.com/TomGillen/legion). The implementation is based heavily | ||
on the new Unity ECS Transformation layout. | ||
|
||
## Usage | ||
|
||
### TL;DR - Just show me the secret codes and incantations! | ||
|
||
See [examples/hierarchy.rs](examples/hierarchy.rs) | ||
|
||
```rust | ||
#[allow(unused)] | ||
fn tldr_sample() { | ||
// Create a normal Legion World | ||
let mut world = Universe::default().create_world(); | ||
|
||
// Create a system bundle (vec of systems) for LegionTransform | ||
let transform_system_bundle = TransformSystemBundle::default().build(); | ||
|
||
let parent_entity = *world | ||
.insert( | ||
(), | ||
vec![( | ||
// Always needed for an Entity that has any space transform | ||
LocalToWorld::identity(), | ||
// The only mutable space transform a parent has is a translation. | ||
Translation::new(100.0, 0.0, 0.0), | ||
)], | ||
) | ||
.first() | ||
.unwrap(); | ||
|
||
world.insert( | ||
(), | ||
vec![ | ||
( | ||
// Again, always need a `LocalToWorld` component for the Entity to have a custom | ||
// space transform. | ||
LocalToWorld::identity(), | ||
// Here we define a Translation, Rotation and uniform Scale. | ||
Translation::new(1.0, 2.0, 3.0), | ||
Rotation::from_euler_angles(3.14, 0.0, 0.0), | ||
Scale(2.0), | ||
// Add a Parent and LocalToParent component to attach a child to a parent. | ||
Parent(parent_entity), | ||
LocalToParent::identity(), | ||
); | ||
4 | ||
], | ||
); | ||
} | ||
``` | ||
|
||
See [examples](/examples) for both transform and hierarchy examples. | ||
|
||
### Transform Overview | ||
|
||
The Transform and Hierarchy parts of Legion Transform are largely separate and | ||
can thus be explained independently. We will start with space transforms, so for | ||
now completely put hierarchies out of mind (all entities have space transforms | ||
directly from their space to world space). | ||
|
||
A 3D space transform can come in many forms. The most generic of these is a | ||
matrix 4x4 which can represent any arbitrary (linear) space transform, including | ||
projections and sheers. These are not rarely useful for entity transformations | ||
though, which are normally defined by things like | ||
|
||
- A **Translation** - movement along the X, Y or Z axis. | ||
- A **Rotation** - 3D rotation encoded as a Unit Quaternion to prevent [gimbal | ||
lock](https://en.wikipedia.org/wiki/Gimbal_lock). | ||
- A **Scale** - Defined as a single floating point values, but often | ||
**incorrectly defined as a Vector3** (which is a `NonUniformScale`) in other | ||
engines and 3D applications. | ||
- A **NonUniformScale** - Defined as a scale for the X, Y and Z axis | ||
independently from each other. | ||
|
||
In fact, in Legion Transform, each of the above is it's own `Component` type. | ||
These components can be added in any combination to an `Entity` with the only | ||
exception being that `Scale` and `NonUniformScale` are mutually exclusive. | ||
|
||
Higher-order transformations can be built out of combinations of these | ||
components, for example: | ||
|
||
- Isometry: `Translation` + `Rotation` | ||
- Similarity: `Translation` + `Rotation` + `Scale` | ||
- Affine: `Translation` + `Rotation` + `NonUniformScale` | ||
|
||
The combination of these components will be processed (when they change) by the | ||
`LocalToWorldSystem` which will produce a correct `LocalToWorld` based on the | ||
attached transformations. This `LocalToWorld` is a homogeneous matrix4x4 | ||
computed as: `(Translation * (Rotation * (Scale | NonUniformScale)))`. | ||
|
||
Breaking apart the transform into separate components means that you need only | ||
pay the runtime cost of computing the actual transform you need per-entity. | ||
Further, having `LocalToWorld` be a separate component means that any static | ||
entity (including those in static hierarchies) can be pre-baked into a | ||
`LocalToWorld` component and the rest of the transform data need not be loaded | ||
or stored in the final build of the game. | ||
|
||
In the event that the Entity is a member of a hierarchy, the `LocalToParent` | ||
matrix will house the `(Translation * (Rotation * (Scale | NonUniformScale)))` | ||
computation instead, and the `LocalToWorld` matrix will house the final local | ||
space to world space transformation (after all it's parent transformations have | ||
been computed). In other words, the `LocalToWorld` matrix is **always** the | ||
transformation from an entities local space, directly into world space, | ||
regardless of if the entity is a member of a hierarchy or not. | ||
|
||
### Why not just NonUniformScale always? | ||
|
||
NonUniformScale is somewhat evil. It has been used (and abused) in countless | ||
game engines and 3D applications. A Transform with a non-uniform scale is known | ||
as an `Affine Transform` and it cannot be applied to things like a sphere | ||
collider in a physics engine without some serious gymnastics, loss of precision | ||
and/or detrimental performance impacts. For this reason, you should always use a | ||
uniform `Scale` component when possible. This component was named `Scale` over | ||
something like "UniformScale" to imply it's status as the default scale | ||
component and `NonUniformScale`'s status as a special case component. | ||
|
||
For more info on space transformations, see [nalgebra Points and | ||
Transformations](https://www.nalgebra.org/points_and_transformations/). | ||
|
||
### Hierarchies | ||
|
||
Hierarchies in Legion Transform are defined in two parts. The first is the | ||
_Source Of Truth_ for the hierarchy, it is always correct and always up-to-date: | ||
the `Parent` Component. This is a component attached to children of a parent (ie | ||
a child 'has a' `Parent`). Users can update this component directly, and because | ||
it points toward the root of the hierarchy tree, it is impossible to form any | ||
other type of graph apart from a tree. | ||
|
||
Each time the Legion Transform system bundle is run, the | ||
`LocalToParentPropagateSystem` will also add/modify/remove a `Children` | ||
component on any entity that has children (ie entities that have a `Parent` | ||
component pointing to the parent entity). Because this component is only updated | ||
during the system bundle run, **it can be out of date, incorrect or missing | ||
altogether** after world mutations. | ||
|
||
It is important to note that as of today, any member of a hierarchy has it's | ||
`LocalToWorld` matrix re-computed each system bundle run, regardless of | ||
changes. This may someday change, but it is expected that the number of entities | ||
in a dynamic hierarchy for a final game should be small (static hierarchies can | ||
be pre-baked, where each entity gets a pre-baked `LocalToWorld` matrix). | ||
|
||
## This is no good 'tall, why didn't you do is <this> way? | ||
|
||
The first implementation used Legion `Tags` to store the Parent component for | ||
any child. This allowed for things like `O(1)` lookup of children, but was | ||
deemed way too much fragmentation (Legion is an archetypical, chunked ECS). | ||
|
||
The second implementation was based on [this fine article by Michele | ||
Caini](https://skypjack.github.io/2019-06-25-ecs-baf-part-4/) which structures | ||
the hierarchy as explicit parent pointer, a pointer to the first (and only | ||
first) child, and implicitly forms a linked-list of siblings. While elegant, the | ||
actual implementation was both complicated an near-impossible to multi-thread. | ||
For example, iterating through children entities required a global query to the | ||
Legion `World` for each child. I decided a small amount of memory by storing a | ||
possibly-out-of-date `SmallVec` of children was worth sacrificing on parent | ||
entities to make code both simpler and faster (theoretically, I never tested | ||
it). | ||
|
||
A lot of other options were considered as well, for example storing the entire | ||
hierarchy out-of-band from the ECS (much like Amethyst pre-Legion does). This | ||
has some pretty nasty drawbacks though. It makes streaming entities much harder, | ||
it means that hierarchies need to be special-case serialized/deserialized with | ||
initialization code being run on the newly deserialized entities. And it means | ||
that the hierarchy does not conform to the rest of the ECS. It also means that | ||
Legion, and all the various optimizations for querying / iterating large numbers | ||
of entities, was going to be mostly unused and a lot of global queries would | ||
need to be made against the `World` while syncing the `World` and out-of-band | ||
data-structure. I felt very strongly against an out-of-band implementation | ||
despite it being simpler to implement upfront. | ||
|
||
## Todo | ||
|
||
- [ ] Hierarchy maintenance | ||
- [x] Remove changed `Parent` from `Children` list of the previous parent. | ||
- [x] Add changed `Parent` to `Children` list of the new parent. | ||
- [x] Update `PreviousParent` to the new Parent. | ||
- [x] Handle Entities with removed `Parent` components. | ||
- [x] Handle Entities with `Children` but without `LocalToWorld` (move their | ||
children to non-hierarchical). | ||
- [ ] Handle deleted Legion Entities (requires | ||
[Legion #13](https://github.com/TomGillen/legion/issues/13)) | ||
- [x] Local to world and parent transformation | ||
- [x] Handle homogeneous `Matrix4<f32>` calculation for combinations of: | ||
- [x] Translation | ||
- [x] Rotation | ||
- [x] Scale | ||
- [x] NonUniformScale | ||
- [x] Handle change detection and only recompute `LocalToWorld` when needed. | ||
- [x] Multi-threaded updates for non-hierarchical `LocalToWorld` computation. | ||
- [x] Recompute `LocalToParent` each run, always. | ||
- [ ] Transform hierarchy propagation | ||
- [x] Collect roots of the hierarchy forest | ||
- [x] Recursively re-compute `LocalToWorld` from the `Parent`'s `LocalToWorld` | ||
and the `LocalToParent` of each child. | ||
- [ ] Multi-threaded updates for hierarchical `LocalToWorld` computation. | ||
- [ ] Compute all changes and flush them to a `CommandBuffer` rather than | ||
direct mutation of components. | ||
|
||
## Blockers | ||
|
||
- Legion has no ability to detect deleted entities or components. | ||
[GitHub Issue #13](https://github.com/TomGillen/legion/issues/13) | ||
# Bevy Transform | ||
|
||
This crate is largely a 1:1 port from [legion_transform](https://github.com/AThilenius/legion_transform) (ecs: legion, math: nalgebra) to bevy (ecs: bevy_ecs, math: glam) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,8 @@ | ||
use bevy_ecs::Entity; | ||
use bevy_property::Properties; | ||
use shrinkwraprs::Shrinkwrap; | ||
|
||
#[derive(Shrinkwrap, Debug, Copy, Clone, Eq, PartialEq, Properties)] | ||
#[shrinkwrap(mutable)] | ||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Properties)] | ||
pub struct Parent(pub Entity); | ||
|
||
#[derive(Shrinkwrap, Debug, Copy, Clone, Eq, PartialEq)] | ||
#[shrinkwrap(mutable)] | ||
#[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
pub struct PreviousParent(pub Option<Entity>); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters