Closed johnny-smitherson closed 5 months ago
There is nothing stopping you from using multiple cameras with a single floating origin. The only thing that matters is the distance of each camera from the floating origin. If you have 2 player split screen, you might place the floating origin at the center point between the two players, and prevent them from moving more than a kilometer apart. The floating origin has no coupling with cameras at all - it only defines what to use as the origin for computing the GlobalTransform
of all entities. It just happens to be that when you have a single camera, making that entity be your floating origin is what you want.
The only way to support multiple floating origins would be if each floating origin was operating on an entirely unique set of objects - they could not overlap. So yes, you could use renderlayers for this, but only in cases where an object is on a single layer - it's possible for objects to be on many layers. This would require you to effectively have two copies of everything in your world.
For the case of the minimap or similar, you can simply add the IgnoreFloatingOrigin
component, and work with those objects as if they were in a normal bevy transform system.
I'm not opposed to this feature on principle, but I wouldn't want to merge it if it greatly increased complexity or impacted performance.
Thanks for the clarification, I understand things better now.
you might place the floating origin at the center point between the two players, and prevent them from moving more than a kilometer apart
Yep, tried it and it works great as long as all the cameras are around the same grid cells as you said. Still, I would like to try and work around this limitation. My use case would also be a "spectator mode" where you watch 4-8 players that are spread out further than just a few cells. The ability to spectate any set of players/things in the universe in small pop-up windows is also interesting.
This would require you to effectively have two copies of everything in your world.
Actually, since the cameras would be so far apart, it makes sense to have a different distance culling and level of detail for each object vs. each camera - so object duplication and one-hot render layers absolutely make sense here.
The only way to support multiple floating origins would be if each floating origin was operating on an entirely unique set of objects - they could not overlap.
Ah, makes sense, GlobalTransform
cannot have two values for the same object, you have to pick one or the other. I wonder if I can override the GlobalTransform in the camera's render pipeline extract stage, to get around the "no overlap" limitation - will play with that later, through I think it's low priority considering the "different level of detail" issue.
I wouldn't want to merge it if it greatly increased complexity
I went ahead and added a const L: u8=0
generic to everything, to see how that would look like. The plugins, the grid cells, the reference frames, the floating origin, the ignore marker, everything.
Because of the default value of the generic, most of the examples/tests/demo code does not change. Still, there are some structs that were not generic, and now are; and Rust doesn't want to infer the default value of L
if there's no P
there too (or I don't know how to do it).
Here are the only structs that require adding explicit ::<0>
to keep current functionality:
FloatingOrigin
--> FloatingOrigin::<0>
(wasn't generic before)IgnoreFloatingOrigin
--> IgnoreFloatingOrigin::<0>
(wasn't generic before)GridTransformReadOnly<i128>
--> GridTransformReadOnly<i128, 0>
(was generic, but the bevy? macro that generates the struct doesn't preserve the default value for L from the original - need upstream fix)I think we could add the P
generic parameter to the floating point origin and ignore marker. This would also guard better against accidentally using different precisions - through this would be a breaking change. The alternative is to rename FloatingOrigin
into FloatingOriginEx
and then have a typedef FloatingOrigin = FloatingOriginEx::<0>
- this would mean no breaking change, but more confusion?
Here is the draft PR to see what it'd look like. #18
What do you think?
Another idea - you could have aWorld
for each player, but have all worlds extracting to the same render world? Instead of adding complexity to the floating origin plugin, you could let each world be independent and handle the world -> camera connection as part of a custom render plugin. This would be easier in the big picture, potentially, because you don't have to worry about each view of the world stomping on each other. This would likely also be easier for working with other 3rd party plugins, as they wouldn't need to know anything about this multi-world setup.
As a plugin author, this is what I would prefer. It's impossible for me to have a plugin work for everyone's use case if I need to start adding those implementation details inside the plugin.
I'm also not super keen on adding more generics, they tend to make usage and errors pretty confusing, especially if they end up in the public API.
In fact, now that I think about it, you could actually have independent layers today without adding another generic, if you use a new generic type for the grid precision. You could newtype the integer you want to use and implement the trait. e.g. FloatingOriginPlugin<MyI64<1>>
, FloatingOriginPlugin<MyI64<2>>
you could have a World for each player
I can have more than 1 World?? Yeah that sounds much better
https://github.com/bevyengine/bevy/discussions/2237
https://github.com/bevyengine/bevy/discussions/7893
https://github.com/bevyengine/rfcs/pull/16
https://github.com/bevyengine/rfcs/pull/43
not yet, but maybe soon.
you could actually have independent layers today without adding another generic
Great idea - but I think I'd still need a way to differentiate between the FloatingOrigin instances, and a way to map all the ignore markers.
Writing an example file for the horrowshow above, I can see it's not very friendly to work with, lots of IgnoreFloatingOrigin::<X>
to add everywhere.
Actually, since you were talking about many-World, I guess I will try and spawn a bunch of threads and run separate "App" instances on each.
If that doesn't work out, I can always execute many instances of the game exe with different parameters. It'll be a bunch of OS windows instead of bevy UI TargetCamera, but it'll do until they add some kind of support for multi-World
Thanks for the conversation, very useful, much appreciated
You can definitely have multiple worlds right now - bevy's pipelined renderer runs in another world, and data is extracted from the simulation world into the render world at the sync point. It's as simple as sticking a World
inside a resource. The idea is that you would have an independent simulation in each world, run the schedule one frame, then extract the renderable stuff out into the single render world.
In your case, each world would be running the simulation from some entity's point of view, and you would be extracting out the GlobalTransform
s into the render world, handling renderlayers and whatnot. The main world would be nothing more than this glue, as well as a place to store all the independent worlds.
ok i will try it and submit example file with a demo and a bunch of cameras
Has this been attempted to be used with 2 cameras with different render passes?
Here are some use cases: bevy split screen example and trackball + minimap.
Adding 2 objects with the
FloatingOrigin
component triggers panic.I have not looked too closely at what this would all entail, but I do see
https://github.com/aevyrie/big_space/blob/2eb87f948d362b8d18ab77ac77bb7b7441482ee4/src/reference_frame/local_origin.rs#L443
This "local floating origin" value that needs to be updated on all the reference frames would need to be computed for each of the cameras/floating origins we want to render from.
And I guess I'd go down the rabbit hole from there, using the correct instance of the data when the correct camera is rendering, right?
Also, bevy seems to only give us
32 RenderLayers
for control of what actually shows up on the cameras - would we need to put the different "local floating origin" recomputations on different layers, or could this be done by changing the data on the fly when doing each camera's render pass?Finally, is such a feature desired in the lib?