# Transform Overhaul
This design doc follows [a previous attempt to create a design doc for this work](https://hackmd.io/MGeT-wYJSPSpBM7XGn7qug), changing the structure to provide better context and make the work more actionable.
## Status Quo
A `Transform` stores the position, rotation and scale of a single game object. This information is "relative" to any parent that the object might have: these values are propagated down the entity hierarchy and combined into a final `GlobalTransform`, capturing the absolute position, rotation and scale of the object within the game world.
Historically, Bevy used `Transform` + `GlobalTransform` everywhere: for 3D, 2D and UI. As a result `Transform` stores a `Vec3` for position, a `Vec3` for scale, and a `Quat` for rotation, while `GlobalTransform` uses a `Affine3A` to represent the combined transform matrix and speed up transform propagation.
2D is handled by rendering objects flat onto the XY plane, and then using an orthographic projection camera pointing at this plane.
UI is handled similarly, although the coordinate system is in screenspace (to keep the UI in place while you move the camera) and y-flipped to align with standard top-left origin conventions for UI.
In [#16615](https://github.com/bevyengine/bevy/pull/16615), UI was converted to use dedicated `UiTransform` and `UiGlobalTransform` types.
## Problems
Bevy has a number of challenging problems to solve when it comes to representing positions! While we should not attempt to solve all of them at once, it is helpful to tackle closely entangled problems in a single design pass.
A brief summary of the open thorny problems with transforms follows, along with our consensus for which problems should be tackled in this working group vs put off for now.
The problems with `Transform` in 2D are the most pressing, and are laid out in [#7876](https://github.com/bevyengine/bevy/issues/7876) and
[#2548](https://github.com/bevyengine/bevy/issues/2548).
### Problem: 2D rotations should not require quaternions
Rotating an object in 2D should be easy. In Bevy, it's not.
As [demonstrated by our 2D rotation example](https://github.com/bevyengine/bevy/blob/main/examples/2d/rotation.rs), even simple tasks require understanding what a quaternion is and specifying which axis we're rotating around.
If users gets this wrong, their objects are stretched, squashed or even disappear in surprising ways.
This is also inefficient: a `Quat` requires four f32, when only one would suffice for the vast majority of sprites.
## Problem: 2D objects should not have a z-scale
Because we are modelling our 2D game objects with `Transform`, we can change their Z-axis scale as well!
This seemingly has no effect, unless you set their z-scale to zero, in which case the object suddently disappears.
However, as [#4149](https://github.com/bevyengine/bevy/issues/4149) complains, this actually has a subtle and surprising effect on the relative ordering of sprites by interacting with z-based sorting.
## Problem: Z-based layering is insufficient
Classically, 2D games have controlled the relative order of sprites by setting a `z` coordinate: either as part of a fully 3D position / transform or as a seperate field.
This is a largely functional and familiar solution, but it comes with a few problems:
- encourages magic numbers, requiring discipline on behalf of the developer to maintain consistency
- even when constants are used, the meaning of the z-coordinate is unclear when inspecting values in asset files or inspectors
- leads to z-fighting bugs, where two sprites share the same z coordinate and fight each other
- camera culling can result in arbitrarily not rendering sprites when they fall outside of the orthographic camera's viewing frustum
- this is a leaky abstraction, and very hard for beginners to debug
[#1275](https://github.com/bevyengine/bevy/issues/1275) discusses a better way: leaning on the concept of "layers" from 2D art tools like Photoshop or Inkscape. [#19463] proposes an initial solution to this problem.
## Out-of-scope: UI layering is confusing and inconsistent
Similar problems arise in UI when attempting to control the relative layering for UI nodes that overlap.
However, the solution there is based on web standards, works well enough, and has different enough problems that a unified solution should only be attempted after very careful consideration.
For now, we'll leave it alone.
## Out-of-scope: transforms for gameplay
When you first begin creating games, it makes sense to have a single source of truth for information about where objects are.
And for some applications, this works fine! However, this can break down if:
- you need to operate using fixed timesteps (improves robustness to frame-time variation, very common for simulation or networking)
- simply updating the `Transform` during your fixed update will lead to perceptible choppiness
- interpolating correctly is [surprisingly nuanced](https://github.com/Jondolf/bevy_transform_interpolation).
- you want to decouple visual animations from in-game position
- this is related to but distinct from using simplified colliders rather than using a full mesh for collision detection
- you want to model space using discretized steps (common for tilemaps)
While these types are easy enough to [create externally](https://docs.rs/avian3d/latest/avian3d/physics_transform/index.html), the current status quo presents a pitfall (and time sink) for beginners,
and limits cross-crate compatibility.
This problem is not closely tied to resolving 2D transform headaches, and can be tackled seperately.
### Out of scope: generic `Transform` types for floating point or f64 support
As discussed in [#1680](https://github.com/bevyengine/bevy/issues/1680), a surprising number of our users are interested in unusual transform types: f64 for very large games, fixed point to try to avoid issues with numerical precision, even more exotic things for supporting 4D game worlds...
While supporting these users directly is out of scope, it would be good to have a clearly documented pattern that they can use, allowing them to represent positions in their game world however they want without throwing away Bevy's rendering infrastructure or having to fork the engine and multiple ecosystem crates.
### Out of scope: refactoring how `Transform` is stored and accessed
Some users feel that storing position/rotation/scale information on the same component is suboptimal for performance and [ergonomics reasons](https://github.com/bevyengine/bevy/issues/6006).
This change is very controversial, and has been ruled out of scope by Cart.
### Out of scope: disable transform propagation
[#1780](https://github.com/bevyengine/bevy/issues/1780) and [#9228](https://github.com/bevyengine/bevy/issues/9228) lay out a relatively common request: allow users to opt out of some or all of transform propagation.
While this may be reasonable, it doesn't need to be tackled at the same time.
## Solutions
### `GlobalTransform` as a foundational interface
At the end of the day, we need to be able to compare objects of all sorts to each other in a unified space.
This is most essential for rendering: we cannot possibly hope to rewrite our rendering code to be generic over the choice of transform type in a meaningful way.
Instead, we propose using `GlobalTransform` as our lingua franca.
If an object wants to be rendered, it must have a `GlobalTransform`, and rendering *only* cares about this type.
#### Open question: mixed transform hierarchies
There are a number of important cases where users might plausibly have entity hierarchies with differing transforms:
- world-space UI, like HUDs
- world space UI elements rendered using sprites
- Paper Mario-style sprites
- decals
- billboarding
Ideally composing these entities should Just Work in an intuitive fashion, but what does that mean precisely? How can we achieve that in code?
### Dedicated `UiTransform`
[#16615](https://github.com/bevyengine/bevy/pull/16615) refactored our UI solution to improve performance, fix bugs with animation and picking and generally reduce tech debt.
This also comes with its own `UiGlobalTransform`, to accumulate inherited positions.
### Open question: naming consistency
Should this be `TransformUi` and `GlobalTransformUi` instead?
### Dedicated `Transform2D`
A dedicated 2D transform type is fairly simple to define:
```rust
struct Transform2d {
position: Vec2,
rotation: f32,
scale: Vec2,
}
```
This saves data, prevents a number of footguns and avoids the dreaded quaternion.
Notably, unlike in many other solutions, we are not storing information about how to sort 2D objects using an integrated `z` element of position.
This design is confusing to beginners, and permits a number of meaningless operations, increasing the surface area for errors.
This could be a simple `layer: f32` field, but we think that we can do better. See below!
#### Open question: `GlobalTransform2d`?
Like with UI, we probably need a way to be able to check the absolute position of 2D elements.
However, simply using `GlobalTransform` is probably sufficient:
the primary challenges with `Transform` come from setting and modifying values,
not reading them.
Omitting a `GlobalTransform2d` eases migration, reduces code and saves memory on every sprite.
However, adding it might speed up transform propagation as we could do this entirely in 2D.
### `Transform` -> `Transform3d` rename
Why should 3D get all the nice things? If `Transform2d` is good enough for 2D users, shouldn't we use `Transform3d` for consistency?
Pros:
- steers users towards `Transform2d`
- consistent
- makes 2D feel like an equal first-class citizen
- makes it very clear whether objects are intended to be positioned in 2D or 3D
Cons:
- slightly longer
- requires a breaking change
#### Open question: naming conventions
We could pick a different, still consistent naming convention and get all of these benefits!
Note that we cannot pick `2dTransform` or similar: Rust will not let you start identifiers with numbers.
### Better 2D layering
Moving away from simple z-coordinates for 2D layering resolves a number of problems: no more surprising culling, clearer semantics, reduced z-fighting. But it also unlocks a number of more sophisticated new features!
[#19463](https://github.com/bevyengine/bevy/pull/19463) proposes a complex but comprehensive design to support a number of distinct ordering solutions.
These are designed as optional components, rather than as a field on `Transform2d`. This allows for users to select their preferred solution, and allows us to
#### Z-Index
The most basic sorting strategy, relying on a signed integer for manually ordering objects.
The use of integers prevents surprising problems with floating points' limited precision, although it can force users to suddenly have to "shift" coordinates if too many intermediate layers are added.
#### Y-Sort tie breaking
2D games with pseudo-perspective commonly use the Y axis to make objects higher in the world appear behind objects that are lower.
This is insufficient as a tie breaker, but it is a very helpful tool in its own right.
#### Sort bias
An arbitrary bias that is either added to Y transform or used as a secondary sort value.
This allows for small "relative" tweaks to be made to quickly resolve problems.
#### Open question: hierarchy and rendering order
Should children always appear above their parents by default? This is how it works with UI, and generally aligns with artist expectations and has a major benefit of completely eliminating Z fightning within a hierarchy..
Engines like Godot use this as a tie breaker between items in the same layer by rendering each children in order, depth first, before rendering to the next item.
User friendliness and predictability mostly depends on the child ordering API and quality of our inspection tooling.
#### Open question: ZIndex naming
This currently conflicts with `ZIndex` used in `bevy_ui`.
#### Open question: unification with UI
Can UI use the same paradigm? Should they?
The existing solution mostly works (relative)
#### Open question: usage in 3D
When rendering objects onto the same plane in 3D (e.g. for diagetic HUDs), the same problems arise. Can we design a solution that Just Works there too?
## Implementation strategy
This section lays out the PRs that should be made in order. This describes a tree data structure: PRs are only dependent on each other if they are nested.
- [x] [seperate UI transforms](https://github.com/bevyengine/bevy/pull/16615)
- [ ] docs PR explaining that `GlobalTransform` is the shared rendering interface
- [ ] create a `Transform2d` type
- [ ] migrate rest of the engine to `Transform2d`
- [ ] rename `Transform` to `Transform3d`
- [ ] 2D layering solution
- [ ] rename `UiTransform` and `UiGlobalTransform` to match chosen naming convention
- [ ] create an `TransformF64` example teaching users how to use `GlobalTransform` as a common interface