-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
[Merged by Bors] - Use Affine3A for GlobalTransform to allow any affine transformation #4379
Conversation
To be clear, the goal isn't to allow mixing |
This change would also allow for changing |
Sorry for being a bit negative here, but I personally feel like this makes Besides that, I'm not really seeing a use case where I would need to selectively use one transform type over the other. I would rather think about the requirements of my app and then decide which type of global transform to use across the board (while having propagation work in either case). Do you have a specific example for how this might be used? |
It should probably be noted that this would also make GlobalTransform 80 bytes over its current 48, which may have a stronger negative effect on cache perf for the type. |
@CptPotato @james7132 The I've removed the My use case is a 3d modeler to support interactively stretching shapes along arbitrary axes. SRT can only represent scaling along the model's cardinal axes. Once the shapes are stretched, the change can be baked into the vertices, but it hurts performance to have to update meshes every frame while the user is performing the operation, and it's entirely unnecessary, because transforms are converted to matrices before rendering, which can represent this operation. EDIT: With the latest change, transforms propagate as they did before, since all |
@cart it seems like @HackerFoo wants to be able to do shears, if I understood correctly. I was also fiddling in Blender and noticed that the exported matrix transforms are not exact. I’m not certain it uses matrix representations throughout the hierarchy but it could well be an indication of that. |
Another option is to use a pub struct MyTransform {
pub translation: Vec3,
pub rotation: Quat,
pub scale: Mat3,
} This is what I've settled on for my app. This representation allows shear/arbitrary scaling while keeping rotations easy to work with. It requires 3 (translation) + 4 (rotation) + 9 (scale) = 16 floats, the same as |
Here's a quick demo to show how non-axis-aligned scaling is useful in Noumenal (click for video): With only axis-aligned scaling, you can't create diamonds from cubes. Worse, if a shape's local axes become misaligned with what the user thinks they should be, non-uniform scaling gives unexpected results. There are algorithms to automatically align axes to a model, but they are complicated, and not guaranteed to be correct. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’ve only skimmed this so far. Don’t yet act on this feedback as I think @cart will need to decide what to do. We had discussed elsewhere that Affine3A is better for performance in propagation if just multiplying transforms, and that Affine3A represents shears which the separate representation does not, and that shears are desirable for art. Plus it seemed that other engines support shears. As such my personal conclusion was that we should use the separate representation for Transform and Affine3A for GlobalTransform, and that Transform’s compute_matrix would return an Affine3A.
Then I hadn’t figured out how to support any API breakage due to that. I feel like GlobalTransform should probably use all Vec3A and Mat3A for performance but then we’d have disparity in Vec3A vs Vec3. We could use Vec3A everywhere but there was an argument made that people may be confused by the A or confused that it’s 16 bytes instead of 12 bytes. I didn’t like the proposals to type alias Vec3A to Vec3 because that kind of implicit mapping is the kind of stuff that catches you out that I really don’t like wasting time to have to discover and subsequently remember. Things should be what they say they are in my opinion, and not add cognitive overhead as that is unergonomic.
I’m not sure how to expose the rotation and scale parts from GlobalTransform. It does feel like one has to accept that if you are interested in the scale then you must also take the rotation into account. But you should be able to extract rotation to get directional information. I think your macros covered the forward/back/up/down/left/right cases for Affine3A.
But yeah, this is a very pervasive change and so must be decided upon by @cart .
I agree with @superdump, that a matrix representation like Having only |
I don’t think we should have any enum and I don’t see a reason for generics currently. Just Affine3A. :) |
As much as I don't look forward to all my stuff this will break, I agree this is the right direction. @HackerFoo it would be nice to mention in the changelog/migration section that this is a breaking change for anyone accessing It seems odd that we would now support shear in the |
The benefits of TRS over pub struct MyTransform {
/// translational movement
pub translation: Vec3,
/// rotation around the center
pub rotation: Quat,
/// used for scale, but can also contain rotation
pub matrix: Mat3,
} The So, yeah, deciding what to use for |
If this is eventually merged into Bevy, would it be possible to have some generic I imagine it could work like this (a very rough idea):
I don't know the cost of the data duplication if both There is however one downside I can think of (other than possible efficiency concerns): we will have to decide which encoding for local transformation should be used in |
I haven't actually reviewed this yet, but I am already pretty convinced that this is the right direction for Bevy. I'll try to get this reviewed (and ideally merged) before Bevy 0.8. |
Adding the Needs-Benchmarking tag since this does impact a core system via transform propagation. I'm pretty sure this isn't going to be a potential issue given that Affine3A tends to be faster to multiply through and transform propagation is already random read/write heavy to begin with, but the increased size of the struct does raise concerns over increased cache misses. |
I did a quick comparison for a few transform-intensive systems between this PR and main via the extract_skinned_meshes (717.23us -> 609.56us, ~15% faster) transform propagation (1.69ms -> 1.89ms, ~10.5% slower) It should be noted that transform propagation can be easily parallelized, but skinned mesh extraction cannot, so take these results with a grain of salt. I'm pretty sure the degradation in perf is coming from two sources:
|
(just resolved merge conflicts)
I agree with @superdump. We should make it easier to interact with GlobalTransform / retain as many Transform apis as we can. |
@superdump @cart The difference in So I intentionally tried to restrict |
That said, all the lossless |
I do agree that Affine3A to RST should be avoided, yet we do still have places that rely on that behavior (at least, for now). And I think there will always be valid cases that want that (and are wiling to make assumptions). Might as well embrace that / make it easier. My last commit added docs calling out these constraints (snagged from glam) to the relevant methods. On the topic of using Transform as "the" way to extract RST from GlobalTransform, I'm a bit torn. Clearly, its tied up in "inherited transform component" semantics. But I can see it also filling the role as a nice "core" RST math type. I think for now, lets avoid it where possible (in favor of to_rotation_scale_translation). I also agree that GlobalTransform should largely be a read-only component. That being said, it is the "source of truth" transform component and there are valid reasons to interact with it. Might as well give those cases the best UX we can. There may be future scenarios that eschew Transform entirely (or implement an alternative propagation system that doesn't use Transform). |
I have eliminated all conversions from |
911a400
to
e861074
Compare
bors r+ |
Merge conflict. |
bors r+ |
…4379) # Objective - Add capability to use `Affine3A`s for some `GlobalTransform`s. This allows affine transformations that are not possible using a single `Transform` such as shear and non-uniform scaling along an arbitrary axis. - Related to #1755 and #2026 ## Solution - `GlobalTransform` becomes an enum wrapping either a `Transform` or an `Affine3A`. - The API of `GlobalTransform` is minimized to avoid inefficiency, and to make it clear that operations should be performed using the underlying data types. - using `GlobalTransform::Affine3A` disables transform propagation, because the main use is for cases that `Transform`s cannot support. --- ## Changelog - `GlobalTransform`s can optionally support any affine transformation using an `Affine3A`. Co-authored-by: Carter Anderson <[email protected]>
Pull request successfully merged into main. Build succeeded: |
…evyengine#4379) # Objective - Add capability to use `Affine3A`s for some `GlobalTransform`s. This allows affine transformations that are not possible using a single `Transform` such as shear and non-uniform scaling along an arbitrary axis. - Related to bevyengine#1755 and bevyengine#2026 ## Solution - `GlobalTransform` becomes an enum wrapping either a `Transform` or an `Affine3A`. - The API of `GlobalTransform` is minimized to avoid inefficiency, and to make it clear that operations should be performed using the underlying data types. - using `GlobalTransform::Affine3A` disables transform propagation, because the main use is for cases that `Transform`s cannot support. --- ## Changelog - `GlobalTransform`s can optionally support any affine transformation using an `Affine3A`. Co-authored-by: Carter Anderson <[email protected]>
…evyengine#4379) # Objective - Add capability to use `Affine3A`s for some `GlobalTransform`s. This allows affine transformations that are not possible using a single `Transform` such as shear and non-uniform scaling along an arbitrary axis. - Related to bevyengine#1755 and bevyengine#2026 ## Solution - `GlobalTransform` becomes an enum wrapping either a `Transform` or an `Affine3A`. - The API of `GlobalTransform` is minimized to avoid inefficiency, and to make it clear that operations should be performed using the underlying data types. - using `GlobalTransform::Affine3A` disables transform propagation, because the main use is for cases that `Transform`s cannot support. --- ## Changelog - `GlobalTransform`s can optionally support any affine transformation using an `Affine3A`. Co-authored-by: Carter Anderson <[email protected]>
…evyengine#4379) # Objective - Add capability to use `Affine3A`s for some `GlobalTransform`s. This allows affine transformations that are not possible using a single `Transform` such as shear and non-uniform scaling along an arbitrary axis. - Related to bevyengine#1755 and bevyengine#2026 ## Solution - `GlobalTransform` becomes an enum wrapping either a `Transform` or an `Affine3A`. - The API of `GlobalTransform` is minimized to avoid inefficiency, and to make it clear that operations should be performed using the underlying data types. - using `GlobalTransform::Affine3A` disables transform propagation, because the main use is for cases that `Transform`s cannot support. --- ## Changelog - `GlobalTransform`s can optionally support any affine transformation using an `Affine3A`. Co-authored-by: Carter Anderson <[email protected]>
Objective
Affine3A
s for someGlobalTransform
s. This allows affine transformations that are not possible using a singleTransform
such as shear and non-uniform scaling along an arbitrary axis.Solution
GlobalTransform
becomes an enum wrapping either aTransform
or anAffine3A
.GlobalTransform
is minimized to avoid inefficiency, and to make it clear that operations should be performed using the underlying data types.GlobalTransform::Affine3A
disables transform propagation, because the main use is for cases thatTransform
s cannot support.Changelog
GlobalTransform
s can optionally support any affine transformation using anAffine3A
.