Skip to content

GlobalTransform Rework #5125

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

ManevilleF
Copy link
Contributor

@ManevilleF ManevilleF commented Jun 28, 2022

Fixes #2026
Conflicts with #4379

Objective

Currently, GlobalTransform and Transform are absolutely identical, with duplicate fields, duplicate methods and constructors.
Removing the redundancy and making Transform the internal value of GlobalTransform would remove the duplicate code and restrict GlobalTransform to be a hierarchy component.

Solution

GlobalTransform is now a tuple struct with an inner Transform value. It implements Deref towars its inner type allowing the use for the entire Transform API.
The inner type is not public, meaning that only bevy_transform can edit it. Enforcing the tacit no touch rule on that component.

Notes

@james7132
Copy link
Member

This might be in conflict with #4379.

@ManevilleF ManevilleF marked this pull request as ready for review June 28, 2022 14:35
Copy link
Member

@james7132 james7132 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't fully read through the exact changes, but there's still the std::mem::swap loophole of mutating GlobalTransform. Otherwise this seems to be a shut case on preventing users from arbitrarily changing the transforms of everything.

@ManevilleF
Copy link
Contributor Author

@james7132 The objective is mainly to remove redundancy and to avoid accidental misuse of GlobalTransform by editing its values, so I'm not worried by mem::swap or potential EntityCommands::insert to override the component.

But maybe we do want to allow the users to edit the GlobalTransform and then, implementing DerefMut does the trick

@alice-i-cecile alice-i-cecile added C-Usability A targeted quality-of-life change that makes Bevy easier to use A-Transform Translations, rotations and scales X-Controversial There is active debate or serious implications around merging this PR labels Jun 28, 2022
@alice-i-cecile alice-i-cecile requested a review from aevyrie June 28, 2022 16:37
@alice-i-cecile alice-i-cecile added the M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide label Jun 28, 2022
@alice-i-cecile
Copy link
Member

Initial thoughts:

  1. I really like the deduplication.
  2. We need to benchmark changes that touch transforms aggressively; these are on the hot path.
  3. Users may have valid use cases to modify GlobalTransform themselves; I suspect that bit will be very controversial.
  4. Using Added systems and pulling these out of bundles will be quite painful, because of the delay in states. This feels like perhaps something that hooks could help with? Or perhaps tie into Archetype Invariants #1481 somehow?

@ManevilleF
Copy link
Contributor Author

ManevilleF commented Jun 28, 2022

3. Users may have valid use cases to modify `GlobalTransform` themselves; I suspect that bit will be very controversial.

I find this strange, as the local Transform would then be invalid and the whole hierarchy could be messed up.
But if it is a use case then having DerefMut or even making the inner field public could work and I can put back the transform example

Edit: If changing the GlobalTransform is a thing, it could be considered a feature that would then propagate to the local Transform and to children, just like how the local transform then is applied to the global one etc

4. Using `Added` systems and pulling these out of bundles will be quite painful, because of the delay in states. This feels like perhaps something that hooks could help with? Or perhaps tie into [Archetype Invariants #1481](https://github.com/bevyengine/bevy/issues/1481) somehow?

Maybe it's too soon for that

@HackerFoo
Copy link
Contributor

This change requires that Transform and GlobalTransform have the same representation, which is problematic because they serve different purposes. Transform needs to map well to desired motions for animation and physics, and so rotation and scaling should be separate, as they are using the current SRT representation. GlobalTransform on the other hand is an accumulator for the chain of multiplied transforms. The problem (that #4379 solves) is that the SRT representation that Transform uses can't actually do that in the presence of intermediate nonuniform scale, and so gives the wrong result.

@alice-i-cecile
Copy link
Member

My preference is for #4379 over this PR at this point :)

@ManevilleF
Copy link
Contributor Author

This MR only removes code duplication and confusion on the two transform types.
I'm not making a stand on the inner type, quite the opposite. This lays groundwork for Global transform rework in the most uncontroversial way

@HackerFoo
Copy link
Contributor

HackerFoo commented Jul 2, 2022

The PR converts GlobalTransform to the inner type in many places. This removes the opportunity to define methods and trait implementations specific to GlobalTransform (or restrict operations available on the inner type), and also makes it easier to confuse GlobalTransform with its' inner type, losing the information that it transforms from some local space to world space.

Doing this is the opposite of what I'd like to see, which is using types to avoid confusing different transforms, for example Transform<B, C> * Transform<A, B> = Transform<A, C>. Transforms should be typed like any other function. This would have saved me a lot of time and frustration.

@ManevilleF
Copy link
Contributor Author

The PR converts GlobalTransform to the inner type in many places. This removes the opportunity to define methods and trait implementations specific to GlobalTransform (or restrict operations available on the inner type), and also makes it easier to confuse GlobalTransform with its' inner type, losing the information that it transforms from some local space to world space.

I convert to the inner type so that extraction stages no longer store a GlobalTransform wrapper but an actual Transform, meaning less confusion between components and operational types imo. Ideally extraction types would store directly a Mat4 or Affine3A but I'm just removing boilerplate here.

This MR only does this:

  • A Transform is both as a component stores local transformation, and the transformation operational type.
  • A GlobalTransform is only a component identifying world space transformation

Doing this is the opposite of what I'd like to see, which is using types to avoid confusing different transforms, for example Transform<B, C> * Transform<A, B> = Transform<A, C>. Transforms should be typed like any other function. This would have saved me a lot of time and frustration.

I don't think making Transform generic is a good idea in term of ergonomics.

I'm closing this for now. I don't want this PR to shift focus from #4379

@ManevilleF ManevilleF closed this Jul 3, 2022
@ManevilleF ManevilleF deleted the feat/global_transform_rework branch July 4, 2022 07:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Transform Translations, rotations and scales C-Usability A targeted quality-of-life change that makes Bevy easier to use M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide X-Controversial There is active debate or serious implications around merging this PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Reduce code duplication between Transform and GlobalTransform
5 participants