Improve `OrthographicCamera` consistency and usability
Objective
- Terminology used in field names and docs aren't accurate
window_origindoesn't have any effect whenscaling_modeisScalingMode::Noneleft,right,bottom, andtopare set automatically unlessscaling_modeisNone. Fields that only sometimes give feedback are confusing.- Fixes #5818
- Fixes #6190
Solution
- Make public fields have an effect on every
ScalingMode. They should also be much more intuitive. See changelog - Make
left,right,bottom, andtopprivate with getters; add constructor - Improve consistency in
ScalingMode's arguments - Improve accuracy of terminology:
- "Window" refers to the physical window managed by the desktop environment
- "Viewport" refers to the part of the window (typically all of it) that images are drawn on
- "View frustum" refers to the volume captured by the projection
Changelog
Added
- Constructor using a pseudo builder pattern
- Getters for
left,right,bottom, andtop - Argument to
ScalingMode::WindowSizethat specifies the number of pixels that equals one world unit. 1 pixel = 1 world unit usually makes the camera way too wide. This also makes it more consistent with otherScalingModes - Documentation for fields and enums
Changed
window_originis renamed toviewport_originand now affects everyScalingMode. It also takes a fraction of the viewport's width and height instead of an enum- Removed
WindowOriginenum as it's obsolete ScalingMode::NonetoScalingMode::Fixed, which now has arguments to specify the projection sizeleft,right,bottom, andtopare made privatescaleis applied before updatingleft,right,bottom, andtop. Reading these values will now takescaleinto account- Documentation changes to make terminology more accurate and consistent
Migration Guide
- Instantiate
OrthographicProjectionwith constructor instead. Set the appropriate properties using builder methods - Change
window_origintoviewport_origin; replaceWindowOrigin::Centerwith(0.5, 0.5)andWindowOrigin::BottomLeftwith(0.0, 0.0) - Change
ScalingMode::NonetoScalingMode::Fixed - Replace manual changes to
left,right,bottom, andtopwith arguments inScalingMode::Fixedto specify size andviewport_originto specify offset - Replace reads from
left,right,bottom, andtopwith corresponding getters - Change
ScalingMode::WindowSizetoScalingMode::WindowSize(1.0)
Ping @bzm3r for review.
@alice-i-cecile shouldn't this be tagged with "breaking change"?
Yep, thank you!
I have a few minor suggestions but I like the changes! Worth noting this breaks the current 2d examples as is - they all appear way more zoomed in than they should be. But that's just a matter of adjusting the defaults to be closer to how it was before.
Oh, I didn't notice that. That's probably because I made the default for WindowSize 10 since the camera is usually way too zoomed out by default, but I can change it back.
I’d like to take a look at this if I get time.
I'm trying to add a separate "config" struct for initializing, but it's a bit of a mess (and might be unnecessarily complicated). Maybe using a builder or just a simple constructor would work better?
I'm not sure if there's enough fields to warrant a builder, but there's too many fields to fit into constructor parameters comfortably. Are there any methods that would make configurating the fields ergonomic but also allow defaults?
Unfortunately no; you can't use struct update syntax unless the fields are pub. I think a builder pattern or an elaborate constructor is the way to go.
Thanks for the change! I salute the documenting part which is much welcome ♥️ But I'm more skeptical about the builder pattern refactoring. For one, it feels those two changes are unrelated and should maybe be done in separate PRs. I also personally don't like the pattern so much, mostly as it increases inconsistency with the rest of the Bevy codebase.
I sort of agree with this. Do you have any suggestions for alternatives? Every other method I've looked at has had some other downside. I could also just leave the fields public.
I sort of agree with this. Do you have any suggestions for alternatives? Every other method I've looked at has had some other downside. I could also just leave the fields public.
Because this is the recommended and promoted pattern in the rest of the codebase, yes I would just leave those as public fields for now. We can discuss whether this is the right pattern or not (I doubt we can convince anyone of a change at this point though), but I wouldn't make this object inconsistent unless there's an overwhelming benefit to the builder pattern, which I don't think we showed here.
Because this is the recommended and promoted pattern in the rest of the codebase, yes I would just leave those as public fields for now. We can discuss whether this is the right pattern or not (I doubt we can convince anyone of a change at this point though), but I wouldn't make this object inconsistent unless there's an overwhelming benefit to the builder pattern, which I don't think we showed here.
That's fair. Clearly documenting that l/r/b/t should be for reading only should be fine for now.
I've been kind of busy lately but I have some time this week for some minor fixes and to test examples. This should be ready for review after that.
@xgbwei once this is rebased I'd like to merge it.
Did I do that right?
Rebase looks almost correct: you have an empty scene_viewer file :)
bors r+
Pull request successfully merged into main.
Build succeeded:
- build-and-install-on-iOS
- build-android
- build (macos-latest)
- build (ubuntu-latest)
- build-wasm
- build (windows-latest)
- build-without-default-features (bevy)
- build-without-default-features (bevy_ecs)
- build-without-default-features (bevy_reflect)
- check-compiles
- check-doc
- check-missing-examples-in-docs
- ci
- markdownlint
- msrv
- run-examples
- run-examples-on-wasm
- run-examples-on-windows-dx12