#Next Generation Scenes
1 messages Β· Page 8 of 1
Looks to be a PathType logic error in the parser. It is interpreting palette::Z_AXIS as PathType::Type instead of PathType::Const.
PathType is inherently a bit janky because it relies on Rust module / type naming conventions to determine the PathType
Easy fix for this case
I'm aware of one true ambiguity in the Rust naming rules (foo::Z). You can't know if this is PathType::Const or PathType::Type
Because we need at least two characters to disambiguate. ex: foo::ZZ is a Const and foo::Zz is a Type
I resolve the ambiguity by interpreting it as PathType::Type and requiring {foo::Z} if it happens to be a Const
Having type info in macros would be so dang nice :/
Fix looks like this:
totally a nitpick but you could use is_none_or(..) instead of map<bool>(...).unwrap_or(false) which i usually find clearer
wait
is_some_and
I usually let clippy do this type of refactor for me. I think in maps and unwraps
Pretty sure it has a rule for this case
My brain is "map the result first, then handle the None case"
If it wasn't the default clippy rule I'd definitely just leave it as-is. I think we maybe have too many tools for handling these cases
I like to handle to none case as soon as possible, reading that made me have to double back and rethink which makes sense since it's basically like having an else statement below everything
That being said in practice I do think I end up doing the same thing as you...
Just pushed the BackgroundColor(palette::Z_AXIS) bugfix, fixed a couple of other cases, and added PathType parsing tests:
https://github.com/cart/bevy/commit/162232fef632d9dcd1fece914b4de4c28564cc49
*only if the code is following style conventions
For sure!
Could this default to const instead? Things like Vec3::Y are fairly common
or are associated consts unaffected?
It could be flipped (and associated consts are affected, as they are ambiguous with Enum::Variant). And yeah one-letter const names are likely more common in this case. The rationale behind flipping them is that {foo::X} syntax is a natural disambiguator in BSN if the default "type patch" behavior isn't correct (ex: default to "type patch" and if that isn't right use {}), but there is currently no syntax in bsn to do the reverse (go from a const to a "type patch")
All that is left at this point is quality / prep work:
Yay for docs and tests before
π
not just docs and tests, docs! and tests!
this just in, bsn! {} macro is blocked on docs! {} and tests! {} macros
.bsn asset format blocked on .docx support in Bevy, got it
it is hard to believe, but the amount of days until BSN is 4!
4 factorial, got it
Adding one more TODO item:
Look for a way to keep
impl Default for Handlefor easier piecemeal migrations
Removing Default for Handle is what enables us to impl<T: Default + Clone> Template for T and have impl GetTemplate for Handle (as these two impls conflict with each other)
The blanket impl is critical for making BSN usable with things that don't have a manual Template impl
And the manual GetTemplate impl for Handle is critical for allowing "asset path" templates for handles like Sprite { image: "sprite.png" }
Long term that design is compatible with "always strong" handles (aka remove Handle::Uuid), which I think we probably want to move to.
The current BSN branch removes impl Default for Handle and adds a direct Handle::default function.
But that means that anything that is currently:
#[derive(Component, Default, Clone)]
struct MyComponent(Handle<Image>);
Needs to switch to either:
#[derive(Component, Clone)]
struct MyComponent(Handle<Image>);
impl Default for MyComponent {
fn default() -> Self {
MyComponent(Handle::default())
}
}
Or
#[derive(Component, GetTemplate)]
struct MyComponent(Handle<Image>);
Both of which are pretty disruptive. I don't think pushing people to Templates + BSN that aggressively is the right move
Especially right out of the gate / while we're still building it out
A better option (in the early days) might be something like:
#[derive(Component, GetTemplate)]
struct Sprite {
#[template(HandleTemplate<Image>)]
image: Handle<Image>
}
And remove the impl GetTemplate for Handle impl
Aka it makes bsn / templates more opt-in, at the price of ergonomics / UX for bsn / template users
I wrote Template instead of GetTemplate in a couple places. Fixed!
Today I added a bunch of bsn! tests, renamed Template::build to Template::build_template, and pushed those here
I did the rename because Template is implemented for a lot of types (anything with Default + Clone), which meant that Template::build would be ambiguous with any other build() function. Given that 99.9% of people won't be manually called Template::build, I figured giving it the longer name was the move
Ugh. Zed doesn't highlight the @"scene://base.bsn" syntax correctly. The @ throws off the string highlighting. :"string" too apparently
VS Code highlights it correctly. Just a minor issue in Zed. Might file a bug for it in Zed if/when BSN is merged into main
did just test in rustrover and everything in the macro works great, including go to definition inside expressions
Yeah I tried it in Zed recently (I'm still waiting for a few features before I switch) and it largely worked, with the exception of strings.
It works nicely in VSCode because they use Rust Analyzer's type hints in combination with semantic highlighting. Zed landed semantic highlighting recently, but its still new. Probably a gap there of some kind
It highlights string literals correctly in notepad++ and vim too. Seems to just be Zed having issues so far.
I know a few Bevy fans at Zed; once we ship please do make that issue then send it my way
I know i said id do it after the merge but I should probably do it sooner rather than later. It theoretically breaks all proc macros one could make, not just bsn
Yeah i think its worth reporting as soon as possible
I use Zed. It has some really cool features π
Is the intention that a dynamic .bsn file have the same format as bsn! { ... } So you can just copy-paste the guts of a bsn! { ... } macro into a standalone file, and vice versa?
I guess that dynamic .bsn won't be able to do some things that bsn! { ... } can, like embedding Rust code
TBH it's tempting to just build what I need on top of cart's BSN branch because BSN has correct solutions for things like assets and prefabs already
Biggest immediate question to me is how to use the parser given that it's tied to syn
That's my understanding of the vision π
Exactly. Although nothing is stopping people from building alternative formats
However to enable versioning and migration, the .bsn file format will have versioned imports
So at the very least, you will also need to include the versioned Bevy prelude import
And if you have inline Rust, that obviously won't work
I would welcome this!
We will likely want a second parser implementation to make the limitations and error messages between impls clear
I'll see if I can take a stab at a PoC for dynamic .bsn by writing what I want in bsn! then using cargo-expand to see what it turns into, and going from there
Might use lalrpop for that sweet parsing speed
I rather like peg
@thick slate @split harness ^ as requested
Hope I filled out their issue correctly
It was GvR who got me into peg parsers when we worked on the match statement PEP, since then they have been my parser of choice
LALR(1) is sooooo fast though π
I think I was the one who inspired niko to write lalrpop way back when since I was talking about how great LR parsers were
I will refrain from indulging in parser rwars
Also, as long as you aren't using recursive descent for your arithmetic operator precedence tower, parsing speed shouldn't normally be an issue
Their (Zed's) syntax highlight parser understands &"foo" correctly
I wonder if that'll make it easier to fix somehow
Is the intention to slap #[derive(GetTemplate)] on most reflectable components shortly after BSN lands (possibly behind a feature flag)?
And I assume that PatchGetTemplate and friends will have their own ReflectPatchGetTemplate and such?
For a much older version of BSN I have a recursive descent parser. But at this point its probably best to start from scratch.
Most of the current components auto-impl GetTemplate (and Template), as they implement Default + Clone
We only need to port things that need "real" Template logic (aka feed on the spawn context or the World)
Ohh, I see, nice
In practice, thats generally Handles
But if something is currently Option<Entity> / populated post-spawn, thats a good candidate to be ported to Entity with the EntityReference Template
Ditto for anything that has custom ids managed by a resource
Yeah so I assume there will be heavy use of Reflect, including to potentially call functions
Yup!
Makes sense, it's maximally flexible so you don't run into arbitrary limits. Don't use BSN for untrusted content for sure π
We'll want to populate some sort of dynamic "patch" structure, largely driven by Reflect
Probably could just treat ReflectPatchGetTemplate like any other reflectable trait?
Ex: The reflect field_index -> a Reflect value that can be applied to that field
It would probably be ReflectGetTemplate
But yeah thats the general idea
The goal here is to make this as fast as possible. This might mean skipping Reflect for common values like u32 / String / etc
Some Value::U32(u32), Value::Reflect(Box<dyn Reflect>), etc
But ultimately this still isn't a huge deal, as we'll largely only bake these once to a ResolvedScene
But for large scenes, we still want that optimized. And in a world where we have parameterized scene assets, that will likely involve per-spawn patch resolving. And thats when speed starts to be super critical
Also a reminder that spawning is way slower than it should be right now because we are inserting components individually
Trying to think about the minimal amount I could implement to switch our studio over to BSN. I think it'd probably be transforms, children, handles to arbitrary assets, and including other BSN files. I don't think we need to call any functions right now
Is your feeling that you would spawn a dynamic BSN by something like adding a DynamicBsn(Handle<Bsn>) component to an entity that a spawner system will pick up, like scene spawning does today?
Lalrpop is a genuinely pleasant thing to use for writing parser, so 
As much as I like Jackdaw, its scene format requires quite a bit of work to make usable, and BSN has been designed properly from the start, so it strikes me as best to just focus on BSN
Followup would be to make an all-in-one VFX template. We have our own in-house declarative VFX serialization format with tons of bells and whistles that gets converted to a Hanabi graph when you load it. It would be trivial to make it a BSN template and then we could inline it, allowing each VFX prefab to be one BSN file plus whatever textures and models are needed. Templates are neat!
As much as I've complained I really appreciate all the work that's gone into BSN. There have been so many attempts at scene formats in the past in Bevy and BSN is the only one that doesn't immediately fall over with some serious deficiency when you try to use it in production.
Somehow I missed this. Apparently Zed's semantic tokens is disabled by default. Enabling it to full (combined doesn't work) fixes the highlighting for strings.
Still imo other text editors that don't do a thorough parse (like Vim and Notepad++) still render @"foo" strings properly so I'd still call it a bug even when semantic tokens is disabled
Weird I tried toggling them a week ago and nothing changed for me
Current plan: do a spike on a "micro-BSN" which is just enough dynamic BSN to migrate our studio over. Don't do anything that will prevent enlarging this micro-BSN to full BSN later. Fully migrate when 0.19 is released, basing on top of whatever BSN is merged at that time.
FYI: the proto BSN asset format that's in the bevy_editor_prototypes repo has been quite serviceable for our team, while waiting for non-proto BSN. I hacked a crude version of inheritance into it, and we've extended it in some other hacky ways, but it's still quite close to the structure of the original. Just in case you need some reference material or a starting point @steel oak
@alice-i-cecile understood, we'll try to get this in front of the right people π
Bevy rocks!
https://github.com/zed-industries/zed/issues/51240#issuecomment-4035946064
Reproduction steps Open Zed with Rust source code highlighting Type @"foo" or :"bar" String literals "foo" or "bar" are no longer highlighted Current vs. Exp...
Yay thanks Zed team π
I just hope they see the greater picture outside of bevy. Lotta syntax possibilities with rust procmacros. syntax highlighting like <"foo" is broken too in zed but works in every other editor
but it's nice that this was caught before Zed 1.0 which allegedly is around the corner
@split harness Here's my initial experiment using lalrpop. No rush in looking at this at all! https://github.com/pcwalton/bevy/commit/dba0de5a1e37ad2dd79c25587be0570588f1f633
The biggest problems are:
- Can't do multiple patches yet, just one. I think I need to switch to
impl SceneList? This one I'm not worried about. - The
Wrapperhack is more of an issue. It arises because templates typically bootstrap themselves by callingT::default()with no arguments. This isn't sufficient for reflection-based templates because we don't know the type we're supposed to make aDefaultof! My solution was to make a sort of "neutral" component for bootstrapping,Wrapper::Default, which gets replaced with the actualT::defaultvalue once T is known. This is a total hack and I was wondering if you had a better idea.
Anyway, the next thing will be to add multiple patches. Once that's done, we'll need handles via @ paths, struct support for transforms, children, and BSN assets that can reference other BSN assets. Then it should be dogfoodable!
I don't like that Wrapper hack though and I feel I'm probably missing something.
If all goes well and I implement the above, we should be able to start immediately dogfooding dynamic BSN at my studio by backporting this branch onto 0.19. I intend to do that because we honestly need a working scene format more than we need an editor at this point.
I do have to say that, unfortunately, this effort has reinforced rather than reduced my belief that the editor is fundamentally blocked on BSN. Manipulating BSN ASTs is so radically different from manipulating the ECS that trying to make a non-BSN-based editor will involve such a huge rewrite to migrate to BSN that it's not worth it IMO. Better to focus effort on my dynamic BSN spike.
That doesn't mean that editor UI can't be worked on in the meantime, but an editor framework... yeah, the data model is just too fundamentally different.
Basically the editor is going to be maintaining a bidirectional relationship between an entity (and each component) in the preview scene and the corresponding BSN AST node. This has huge advantages, because it sidesteps the whole "how do you prevent gameplay logic from running in edit mode?" question by simply saying "because you're manipulating proxies of AST nodes, not components themselves". But it's a fundamentally different model from every existing Bevy editor (except maybe Blenvy/Skein, in a weird way).
Existing editors have to deal with the whole "how do you instantiate app-specific components in a generic editor?" problem which fundamentally just isn't an issue with BSN except insofar as you might want app-specific editor UI. But that's a much less thorny problem because people are much more understanding of "your custom editor components must go in a separate crate from your game" than "your entire game must be thrown into a library so the editor can import it, and when you recompile the editor has to be restarted".
I almost feel like the first step toward the Bevy editor would be a generic AST-based BSN viewer that watches the .bsn file for live reload. That would be immensely useful today, wouldn't be very hard to implement on top of my dynamic BSN experiment, and could form the guts of a later Bevy editor without changes.
I like this idea a lot
I've tried to express similar ideas in the past, although perhaps not as concretely as this. But I think this is the right approach; part of the friction is that convincing people that the ultimate source of truth for BSN is not actually ECS is a bit of an uphill slog.
(this surprises me too)
Does that mean that you can't use an editor for projects that use the ECS directly vs. going through BSN?
Since that would seem counter productive
The model that I envision is that ECS is the output: it's the object code, not the source code.
That model is inherently incomplete though, since for the runtime state of a game the source of truth will always be the ECS
The way I currently understand it, when talking about using the ECS directly that would essentially imply doing things at runtime and that would be covered by the inspector. The editor here is only about the part that modifies assets, it's not necessarily the entire thing that typically gets called an editor in other engines.
What do you mean by "not actually ecs" here, as in the ast is the source of truth? I had taken that as a given, but I admit I'm lost in the sauce.
Just as in an application, the source of truth during execution is the object code.
What I mean is that there is some AST-like data structure that represents the "source" of BSN, which then gets compiled into ECS.
Yeah the analogy is clear. I'm very skeptical of why you would arbitrarily limit yourself to doing that though. It seems much more useful to me to have
Editor <-> ECS <- BSN
This AST is different from ECS in that it maintains attributes that are not part of entities and components - things like comments and import statements
Does that mean that editor extensions have to be written as mutations on a BSN AST?
I think that would be a major turn off for devs
When you load a template or prefab in flex, what kind of object does your parser output? Does it produce entities and components directly, or is there some intermediate stage? For exmple, if you get a syntax error and want to print the source line and column for the error message, how do you get that info from looking at a component?
My main reason is that data structures intended for interactive editing tend to have additional baggage that you don't want to keep around at runtime
Shouldn't do, a (limited) ECS world should still be part of the dev feedback loop in-editor. And if it's not, that's a problem like you said.
For example, you may want to keep unresolved asset references - relative paths stay relative and so on.
When you load a template or prefab in flex, what kind of object does your parser output?
It outputs an AST that is annotated with line numbers. If you run the AST, it produces entities & components.
If I modify a value in the inspector though, what value does it provide if I get an error like
my.bsn: 100: invalid value
I want to see parser-specific context if I'm explicitly running BSN. If I'm moving things around in the scene or updating component values, I don't need that
TBH I don't really want to have this argument. Let's just agree to disagree for now. Eventually the editor will get written, and we'll know for sure.
I think these are reasonable points that'd have to be addressed/considered no? π€
Changing the scope of the editor to operate on a BSN AST vs. the ECS sounds like a change with a massive impact crater
One pitfall I've had when turning the architecture for potential editors around in my head has been thinking of the patching mechanism as a potential compression tool rather than a dependency management tool. That did lead me down to ast absolutism of a kind. But I think that's different from "editor world runs a limited ECS architecture and syncs changes to an ast"
Anyway, this isn't #editor-dev
And once the format exists it can be 1. Interpreted and designed around as people like 2. Iterated upon in breaking ways if needed, as long as some kind of migration transform is possible
Right now I want to save my debating energy for the upcoming "what does an abstract template represent" discussion
In any case, the short answer is that I don't think BSN to ECS is round-trippable.
If this is just about retaining comments I don't think it justifies such a large architectural change. There are solutions to that problem which are orders of magnitude easier to solve
Let's leave aside comments, relative import paths, and other "resolved at runtime" things - the prime difficulty is that BSN permits multiple templates to be composed, and this gets flattened in ECS - a given component might be the product of several templates, but once it's in component form that info is lost.
I don't think so either, especially with required components on top of everything else. But I do think there's design decisions you can make editor-side to be godot-style permissable that means the scene format (rarely ever human-read) contains the data that's needed even if there's technically redundant storage of component data.
I mean, I suppose you could make an editor for a subset of BSN that used ECS as the source of truth, and that might be good enough for some people
I think there are easy solutions to that:
- assets authored in the editor produce BSN
- when the BSN is instantiated it produces entities and components
- the entities and components are visualized and moved around by the editor
- when you save the scene, the editor writes BSN that represents the scene + the BSN that represents the assets
I don't think templates are needed to actually represent the scene content (aside from assets), unless you are hand-authoring BSN
If you do it like that, it's roundtrippable, your editor treats ECS as the source of truth, and BSN is the format for assets & storing scenes
In a world where a bsn editor exists, 99.99% of .bsn files will never be read directly by a person outside of git diffs.
Where they will be scrolled past as noise
(I speak from Godot experience)
Let's say I'm using the editor to edit a UI. If I add a button to the scene, should the resulting .bsn file contain:
:button()
Or should it be written as:
Node { ... },
Button,
ButtonVariant::Default,
Hovered,
EntityCursor,
TabIndex,
ThemeBackgroundColor,
ThemeFontColor,
InheritableFont {...},
(These latter are all the components that button adds to the entity)
Probably 2, unless 1 is a link to an existing button scene (either hardcoded or on disk) in which case it'd be a dependency.
Dependencies won't want to be resolved into bsn files like that so an update (i.e. change in feathers styling between bevy versions or change to the button asset) propagates properly
Whole point of it really
Upon saving, do a diff between a default instantiated asset and actual entity, and store that
the editor writes BSN that represents the scene
But that's the crux of the issue, BSN is not an ECS serialisation format. You should not expect there to be a good canon BSN representation of an ECS state, there's many ways to package ECS state into BSN, because BSN is rather expressive, for various reasons.
(a better solution IMO would be to make assets something native to the ECS like with flecs prefabs, which solves this entire issue, but that's probably not gonna happen here)
As Talin said, templates are the big thing, because they're not a constraint imposed by just BSN being the way it is, but one of the goals of an asset format for entities it to be able to build and compose hiearchies and templates of them.
But then there's plenty of other things.
This is, ultimately, editor design more than bsn design.
I do personally think BSN is maybe too complex for the good of the editor, but I don't consider trying to wrangle around the one-directionality of scene -> ECS instantiation a good solution.
The rubber will hit the road, eventually.
Only in the sense that you canβt use the Unity editor to persistently edit objects that you created by manually writing GameObject.Spawn or however itβs spelled in Unity. Which, yes. But also, who cares.
We'll see (and some are seeing already, hi pcwalton)
You can though, you can edit objects while your game is running
Thatβs what BRP is for π
I'm off to sleep π
(Thatβs why I said βpersistentlyβ, to anticipate that objection)
Right, I was operating under the assumption that you'd want to have a single inspector/entity view/... widget that can do both
does this mean I will need to convert all existing .spawn to BSN to be able to edit objects via editor?
Personally the Bevy approach is a breath of fresh air. In Unity all sorts of evil comes from trying to pretend that edit mode and play mode are the same thing. I much prefer just making it front and center and saying no, they arenβt
Also, if you use BRP what does that mean for Rust-authored editor extensions?
Just sounds like such a lot of entirely avoidable complexity, if you just don't make the entire editor go through BSN π
is there anything actually stopping a bevy inspector from doing this though? Unity has remarkably different behavior between play mode and edit mode, where edits during play mode to assets (like prefabs) are saved but runtime objects are not, whereas everything is serialized to the scene in edit mode. that sounds doable with BSN
@leaden dew see ^ this would let you do that
The behavior of Unity's inspector and treeview are exactly the same in editor/play mode. The only difference is that changes during play mode aren't persistent
right, think i lost track of your broader point questioning whether the editor should operate on the BSN AST
Yeah TL;DR it makes more sense to me to have
Editor <-> ECS <- BSN
than to have
Editor <-> BSN -> ECS
As an example of this, consider prefab unpacking. In edit mode, there is an enormous difference between a packed prefab and an unpacked prefab, whereas in play mode there is no such difference. If you change a packed prefab, all non-overridden components are immediately changed in edit mode. Deleting a prefab entity is something you can't do in edit mode (without unpacking it). An unpacked prefab on the other hand is a standalone object; changing the components of the prefab doesn't affect the unpacked prefab.
Now if you had a model where you edit components and you want to make prefab editing sensible, now you're in a pickle, because there is no difference between packed and unpacked prefabs at the component level, and the ECS has no notion of "an overridden component" vs. "a non-overridden component". You need a ton of complexity layered on top of the ECS, or you need to build prefabs and overriding directly into your ECS, or something like that. It is far, far simpler to just separate concerns and say "the edit mode representation is different from the play mode representation". Which is what BSN does.
In my mind, failure to separate concerns like this is exactly why every other scene format in Bevy has failed.
there is an enormous difference between a packed prefab and an unpacked prefab
This doesn't have to be the case
I knew you would say this
and I'm just saying, your solution is ultimately much more complex
By trying to force a unified model on two data models that are fundamentally different, you just add more complexity
The genius of BSN is that it makes front and center that there is a difference
I mean idk about that, I've had this for years, built it as a sole developer, and have people use it, build editors for it (including myself). The API is easy, the concepts are easy (because there's only one representation to reason about), and it addresses pretty much all of the above issues
I assume flecs must have some sort of thing built in where you can say "this component is a mirror of that one except that components X, Y, and Z are different"?
Because otherwise I don't see how it works with prefabs
It supports overriding on the component level
That's the difference, Bevy ECS has no such concept
Right, I know that. But since this is a solution that really simplifies all of this, and since Bevy has full control over the ECS, BSN and the editor, it's worth considering
Well, honestly, if we were to do that then that is a complete redesign of BSN
(that said I think that train has left the station a long time ago)
which we just don't have time to do right now
Fundamentally I think this just comes down to the fact that flecs and Bevy ECS are different. flecs has overriding as a concept and Bevy does not. My argument is that, if you don't have overriding as a concept in the ECS, and we don't, then the BSN approach is the only one that makes sense. Because otherwise I don't see how you make prefabs work.
Right, and in the absence of that, you can still do this ^
I wasn't trying to say that Bevy should adopt Flecs's model (I'd rather have it not do that, it's a differentiator :P). I was just pointing out that the complexity you highlighted was somewhat self inflicted (by Unity)
I thought about that, but I don't like the idea of losing the concept of an overridden property during a round trip
Even if you can reconstruct it again
For example, Unity colors the widgets for overridden properties with a different color iirc
Yeah it does
But IMO that's state that the editor can keep track of, just like how it would track in this design that an entity is an instance of an asset
That way at least all of the BSN complexity is only present at load time and at save time
And you can start development on an editor today
You view it as complexity, I view it as simplicity π I think it's easier to have the data model that you edit have the explicit notion of overriding
Also you realize that that's a challenge, right π
I'm not far from having the infrastructure necessary to start on an editor
Not if the data model is a complex AST that you have to mutate. Unless things changed, Bevy does want the Bevy editor to "just be another Bevy app". That changes significantly if that means "go through the BSN AST first".
I see it as "Every computer science problem can be solved with another layer of indirection"
It's still just another Bevy app. It's just that what you edit isn't the components themselves, but rather an AST.
Speaking of which, I wanted to mention that I think the AST should be a petgraph, at least to start with. Eventually we could have AST nodes as entities, but that has implications that I don't want to think through right now
Idk, I look at the flecs script AST and I wouldn't want to make that the primary interface for people to interact with anything. But maybe BSN will turn out to be much simpler, and this is the first AST that's easy enough to use as front-end API π
I guess I'll have to wait and be proven wrong
I'll definitely have to put a lot of effort into usability
this conversation has been helpful BTW
Oh, something else I thought of:
- There should be some kind of opt-in marker on templates like
SyncInEditMode(name bikeshedding welcome). If this marker is present, then the component will be instantiated from the BSN in the editor and visible in the preview if the component has been linked with the editor. - Things like
Mesh3d,MeshMaterial3d,PointLight, etc. areSyncInEditMode, so you get WYSIWYG in the editor. Some third-party components like Hanabi particle systems can beSyncInEditModeas well, so that they show up. - Most gameplay-related components will not be
SyncInEditMode. This is important because you don't want to have to recompile and reload anything in the editor whenever you change a gameplay-related component. If you add anelemental_weaknessfield to theMonstercomponent, that has no visual appearance in the editor beyond a generic drop-down, so you don't want to have to recompile anything. In fact, a lot of games can be created without linking anything beyond the built-ins to the editor at all. - Some components appear in the editor as gizmos, and those also may not be
SyncInEditMode. Those gizmos can simply operate off the BSN AST. For example, a gizmo that shows a light cone, or that displays a collider as a box, could simply operate off the BSN AST. Or it could operate off aSyncInEditModecomponent, whichever is more convenient. The advantage of usingSyncInEditModeand operating on the component is that you could have the same gizmos appear in play mode; the disadvantage is that you have to have the component linked in to the editor.
I'm having trouble understanding this, although I see it being revised as I read it π
tl;dr I see one of the major advantages of BSN as loose coupling at the ABI level. There should be some way for a generic editor binary to edit BSN corresponding to components it knows nothing about.
Without having to recompile the editor and link it against my game.
There might need to be a way to export a declarative "components schema" -- basically, some file format that the Reflect metadata exports to -- for this to really work.
IIRC Skein has some tool for exactly this, so that Blender can load in the schema corresponding to your game's components.
I'm hesitant to suggest anything here, because my JS editor works so differently in this respect.
How does it work?
(I have a similar solution here for ya: store reflection metadata as entities/components)
in flecs script you can do stuff like
struct Position { // creates Position entity
x = f32 // creates x, y entities
y = f32
}
e {
Position: {10, 20}
}
the first part is mostly syntactic sugar, the desugared version boils down to plain entity/component statements
Well, one approach I have experimented with is making sure that all the game's asset loaders are smart with respect to file watching - so that if an asset file gets touched on disk we can reload the asset without having to restart the game. Then the editor just modifies files.
But I have other approaches, not sure they are relevant to discuss.
I will say that the fun part is all the component-specific gizmos π
Hmm, I think it'd be fairly easy for an editor to have a plugin ABI that lets you pull in &mut World systems at runtime without having to reload anything. fn(&mut World) should be a stable ABI as long as your Bevy versions match up
We really should move this to #editor-dev
OK. It really is kind of both BSN and editor and I feel strongly that BSN should be designed along with the editor, but we can move it there
I agree, but it has more editor tangents than bsn tangents π
yep. skein pulls the type registry introspection info from BRP as json, and sends component data back as basically Vec<Reflect>
I think bsn AST should be expressed as entity-components, and loaded directly as ECS components. Then you can use a ECS editor for BSN. You just need to progressively make the BSN AST editor easier to use (because the default reflect based editor for the AST is NOT easy to use). (And also you can edit the result of prefab unpacking and mirror them back to the BSN, like what Figma does)
If yo view BSN/prefabs just a tool for data compression, then ECS is a more fundemental concept compared to BSN/prefabs. You can have other compressions, so there are no reason bsn should be the single one. And all these are just data and computations, they should all be represented as ECS.
The only serialization format for ECS should be the current Scene format.
Just pushed some BSN changes:
- Scene patching and spawning now have proper error handling (returns Result, queued / deferred spawn failures now log errors instead of panicking)
- Refactored ScenePatch and SceneListPatch spawn logic to be less redundant. Much cleaner now.
- Renamed scene spawning functions on World. They are now
spawn_scene/spawn_scene_list(runs immediately / returns a Result if an error occurs, such as requiring a dependency) andqueue_spawn_scene/queue_spawn_scene_list(queues the scene to be spawned when its dependencies are loaded).
Rad! I'll give this a look / try to answer outstanding questions once the BSN PR is out
Yup this has been my "editor philosophy" from the get-go. Glad to know you're on the same page!
This does seem like a reasonable starting place
I think that 99.99% of users will not need to know the difference. TemplatePatch <-> Component relationship is generally 1:1, and I don't think most people should be using custom templates outside of a few premade ones
This is the BSN model?
For the normal "component patch template" pattern, I believe we can have map-back logic (aka we already have BSN -> Component and nothing is stopping us from building the Component -> BSN mapping)
And this mapping is something we'll want to build to enable "dump the world to a scene file" scenarios, like we currently have with the "old" bevy_scene
Which isn't an immediate priority, but it has come up and I see the value
Yes, Talin asked me what I did, which matches what BSN does (there really aren't that many options when it comes to parsers)
I do think there will be apetite for ECS-driven editor logic / extensions. I think with the "mapback" stuff we can support that scenario, within some limits
I don't disagree with this at all. The thing that feels "wrong" to me is to build an editor that is entirely based off of the BSN AST
^
That is only a first step, and it is notably what editing "flecs script" (the BSN equivalent) is. There is no "writeback" there?
my approach : Editor <-> ECS (BSN subset of ECS, and also can represent ECS. it's kind of a adjoint functor)
Not to rehash the entire discussion, but if it were up to me, I would do this ^
TL;DR, keep BSN at the gates (loading/saving), let the ECS be the source of truth for scene introspection/editing (both in edit mode and play mode), and store assets as BSN
(and keep track of which entities instantiate which assets, which properties are overridden etc)
FWIW this is generally how I envision it too. However where this breaks down is that BSN -> ECS is a "lossy" conversion. We lose a lot of expressive power if we constrain to a roundtrip-able model
Aka the "prefab" vs "flecs script" distinction in flecs
I believe there might be a middle ground that gives us the best of both worlds
Right, that's why I kept assets separate in my description. My thesis is that you do not need that kind of expressiveness when it comes to the contents of a scene (basically just positioning), you only need that for assets where you want to do inheritance etc.
It makes a lot of sense IMO to have an asset editor that speaks BSN
Not saying that you'd never want that expressiveness, but if you do you're much more likely to hand-edit BSN
I see where your head is at. I'm not (yet) in a place where I'm going to strongly push for any of the options discussed here. I would personally start from a "selective syncback" model as I think a unified system is cool and useful
That also happens to be easiest in the early days, as we can start with the "BSN as flecs script" mindset
Yeah fwiw I'm not at all advocating for an approach where you have two separate formats for assets/scene. The output of an editor can still be a bunch of BSN files. I'm just advocating for a solution where the editor accesses the ECS directly for scene contents, since that has lots of advantages
Then wire up syncback where it works
Yup I understand your point and see the advantage there
my take here fwiw is that bevy has always advertised "the engine code is just like game code", and putting bsn between the editor and ecs is going to depart from that majorly. imo if theres an expressivity loss with the ecs, that just means we need to embellish the ecs with more ways to express what it cant, i think bsn assets are "just data" and so whatever they have that the ecs doesnt can just be stuck into components. at the very least, the BSN AST should be entities and relations itself
if theres an expressivity loss with the ecs, that just means we need to embellish the ecs with more ways to express what it cant
this is very much the approach I took with flecs, but not sure if that's compatible with bevy's current direction
i am very very worried about this "use petgraph for the BSN AST manipulation" take ive been seeing. this is very not good and a red flag imo
Yeah I think theres no getting around the fact that people will want to sync back World to BSN (in many different scenarios)
I'm on team "World to BSN". The question in my mind is what the "scope" and implementation of that is
And BSN AST manipulation is unequivocally easier to build in the short term. And its a feature we know we want
BSN is a bit overloaded in that theres the assets variant and the macro variant, i do think that we cannot realistically turn function pointers back into code, but id be happy to be proved wrong. barring that, its just data, which means entities and components
(even in a world where we embrace "BSN limited to what is bi-directional" as our primary editor paradigm, leaving the full BSN featureset to text file or AST editors)
i actually had a conversation with alice ages ago where she asked what game studios needed from a scene format, and i mostly talked about text diffs and merges, and how it should be stable across ser/de. one thing ive thought about a few times is adding span components to entities essentially, which lets them be reassembled in the exact order they were pulled out of the original text
I do suspect that a text based editor is the better move in the short term
But yeah I'm not in a "dictate these parameters" place yet
At the very least the BSN AST and its uses willl show what is required from the ECS as far as missing features or what not.
Everyone should experiment and make their case
/ relying on external text-based editors and hot-reloading when changes are saved
what is the goal of the editor exactly? i thought there was some agreement about MVP being "transform gizmo lets you move things around in a scene and save/load it"
if its just a glorified text editor where you still have to type in the coordinates manually, what exactly is it getting us that we dont already have
Something like this is of course necessary
I'm largely saying that applying BSN changes to the current World is a necessary first step, and provides significant value on its own
necessary first step
"reasonable first step" is a better way to say this
BSN hot reloading is not inherently necessary to a workflow like "load initial BSN file" -> "make changes in ECS" -> "Convert ECS back to BSN" -> "Save BSN file"
(I don't think you need the AST for that, you can just look at the BSN feature set and say: that is missing from the ECS! :p)
@split harness Just implemented multiple patches, wasn't hard. I'm now refactoring to make it possible to have random access into the AST, which is important for an editor. After that, will work on having child nodes in the AST.
Depends on what that question means. If it's:
(1) What is the viewer getting us that the glXF viewer in my glXF crates doesn't have?
- Proper support for overrides.
(2) What is the viewer getting us that Skein doesn't already have?
- Same answer as (1), except also:
- The ability to modify a scene file storing a complex Hanabi effect that was generated from an external tool that doesn't understand Blender and have it live reload.
(3) What is the viewer getting us that .scn.ron or glTF doesn't have?
- Same answer as (1), except also:
- The ability to embed nested scenes.
- The ability to set arbitrary components.
- Proper asset references.
Dammit Discord, you ruined my formatting
FWIW it's clear to me that petgraph doesn't work and I'm going with slotmap instead.
that doesnt sound any better, why not use the ECS
For all the reasons discussed above: lack of support for overrides in the ECS, having to recompile the editor all the time, etc.
and slotmap has overrides? slotmap doesnt have to recompile the editor?
Correct
I would very highly recommend that people actually try using .scn.ron for an actual game. The issues that you run into are not obvious until you actually try to use it and slam headfirst into showstopping issues
we should replace bevy_ecs with slotmap, cus it sounds like its more expressive and powerful (being slightly snarky with this)
I think we're talking past each other.
all im saying is that you can use the ECS in the same way that slotmap can be used
ill go look at the api again just to be sure
yeah this just looks like World
There are two issues here that I are think are being conflated:
- Do you edit components directly and serialize them, or do you edit a higher-level representation that has the notion of overrides and prefabs, etc.?
- Does this higher-level representation live in the ECS or is it separate (e.g. in a slotmap)?
I feel much stronger about (1) than I do about (2).
I'm open to "AST nodes as Entities"
Slotmap is easier for me to get started with. I'm happy to switch it over to the ECS if people want to go that route.
i am not against higher level representations. I am against not dogfooding the ecs when its right there
we used the bespoke render graph thing for ages before switching to the ecs
id like to avoid that if possible
Yeah, I had similar thoughts. This is just a prototype and if folks feel strongly I will move it over to the ECS before making a PR.
Do you edit components directly and serialize them, or do you edit a higher-level representation that has the notion of overrides and prefabs, etc.?
You actually want both, editing a instanced object is non-direct, but you have "clear overrides" which is direct
having two ecs' in tree sounds silly and like a failure
i guess its just EC not ECS
I think y'all might be more sympathetic if you saw how difficult the type tetris I'm having to deal with is π The patch infrastructure is powerful but it is extremely type-heavy and then you add the fact that I'm integrating it with Reflect on top and it requires a lot of experimentation.
I just didn't want to deal with ECS-ifying it yet.
I'm happy to do that once everything is working.
im dealing with my own struggles, currently figuring out render recovery
the RenderAssetUsage Render World is kind of horrible in that when the device has to be recreated, none of the meshes it had in it are in the main world anymore, so i have to reload them
For example I have to do all of this inside an AssetLoader. Uh, how do I access the World inside an AssetLoader again? Do I have to make a sub-World or something? I don't remember and don't want to figure it out when I'm just struggling to get anything working π
yeah you can just create worlds, theres some code showing how to do this in the render world setup
theres also a scratch world
sure thing
let mut world = World::new();
lol
what i dont know about is world merging ergonomics, but i dont imagine slotmap has a thing for that either
You can't make dynamic components at runtime, can you?
(This is not an argument for slotmap, btw, this is an argument against "just edit the ECS")
Can you unregister components and edit them?
not atm i dont think
It's not fundamentally impossible (you can in flecs). To @white lichen's point, these are exactly the kind of dogfooding features that the ECS would have to adopt
That seems like a deal-breaker, because you should be able to modify the component definitions and reload without recompiling your game in such a way that it can be linked against the editor, and without restarting the editor
@karmic rock @white lichen @steel oak
An issue I remember encountering when considering ECS -> BSN writeback is that when spawning things in the ECS, they will go through the "init lifecycle". This could easily change values to something other than what is set at the beginning, and override the intiial user intent.
Additionally, serializing everything isn't on the table, as that doesn't play nicely when patching scenes. We should only serialize what the user has set. This is not inherently the same as "what is not currently default values".
@karmic rock I am curious how you handle this in the context of prefabs and inheritance
To me that feels like a special case of the gnarly problem of how do you prevent logic that is supposed to be for play mode from running in edit mode
The beauty of BSN, to me, is that it says "this is the edit mode data model" and "this is the play mode data model" and they are related, but separate
to be clear: you can encode all of this in the ECS. i agree with what you are saying, i am not claiming that we want to use literally a Camera3d+requiredcomponents to represent a "Camera" configured by the user. The configuration MUST live in the ecs though, as some kind of component
we have to be using the ecs!! we are an ecs engine! thats what its all about lmao its just data!
Those are good points, but I think they're somewhat orthogonal. The editor has to track metadata (which asset is an entity, overridden properties). It could do the same to track what the user has explicitly set- essentially the minimum viable amount of data to reproduce the scene
This is also somewhat of an argument in favor not not using BSN as the source of truth: if instantiating an entity means going through the init lifecycle which creates another entity, I should be able to edit that entity in the editor, and make sure that this edit persists. So that argues in favor of an editor that at least looks a the actual ECS world, not the BSN AST.
if instantiating an entity means going through the init lifecycle which creates another entity, I should be able to edit that entity in the editor, and make sure that this edit persists
I personally don't think this type of "editing future entities" should be valid here
I don't see why I would want that. In fact I think I'd rather have the opposite
Agreed. I'm certain we can come up with cases where that is quite bad
Often times when I have an entity that instantiates another, it's because that second entity encapsulates play-mode-only logic
I think it would be highly counterintuitive to not be able to edit an entity that I can see in the scene, but I could be wrong
For example, when spawning the Player I add a CameraTarget entity which is the entity that the camera looks at. By default that is the player but it can move to focus on other things. I don't want see that CameraTarget in the editor because it's a play-mode-only thing.
It's not really an issue for prefabs and inheritance, because it's natively encoded in the ECS. "What the user did" is what is in the ECS. If a component is not overridden, the entity won't have it and it won't get serialized. The relationship to the asset is encoded explicitly, which is how the entity knows how to find the "inherited" component.
basically the editor will show & edit both BSN and resulting ECS. example: the BSN contains a camera3d, without camera component, and camera3d requires camera component. in Editor it should show camera as read-only, clicking on it should add it to bsn and make it editable.
how do you handle xyz component has one field edited but rest are default -> save -> code is edited to change defaults -> load
One argument in favor of @white lichen's position of AST-nodes-as-entities is that the bidirectional relationship between AST node and instantiated entity could be an actual Relationship
It's authored at the component level, not at the field level. There's nothing fundamentally preventing you from doing it at the field level though (just use reflection), but it's not baked into the ECS. I know of at least one flecs user that has built member overriding on top of Flecs.
I might at some point implement it natively, we'll see
My understanding of prefab inheritance in Flecs is that it is "copy-on-write". If you edit a field on the "base" entity's component from the ECS, how do you know what fields to overwrite on the scenes that inherit from it?
we need to be dogfooding the ecs. we can have "engine code is game code" if the engine is still ecs, just dealing with components and entities as usual. they dont have to be literally The Camera3d or Mesh3d or etc, just entities and components of whatever semantics to encode whatever behavior you want
@split harness ^
One thing to note is that editing handwritten BSN will probably not roundtrip.
Ah yeah @white lichen beat me to it and I missed it π
Just because there's a finite number of patterns that we can handle at the UI level. I don't think this is necessarily a problem. Just something to be aware of.
In short, the granular field-based approach would break down when it comes to normal mutations of component data
Oh, something I didn't consider, pretty printing the resulting BSN will be load-bearing, as we want merge conflicts to be resolvable by hand (holy shit this will be such an advantage over Unity)
i have said this already too
I'm not saying that field-level overriding isn't useful, but it is not as useful as in Unity, where components can have many fields. In ECS it's much more common for the fields to be updated together. E.g. it's unlikely that you only want to update the x field of a component, but still want to receive propagated values for the y field.
here
Yup this is known / I've discussed this a number of times (including recently in this conversation)
i actually said this exact take to alice in DMs 2 years ago
There are solutions to this problem that do not require using an entirely different API for the editor than regular games π
If you've ever had to use Unity's ScriptableObject API... ohhhh boy. I have seen worse APIs but not many.
I'd love to hear them!
im a unity dev of 10 years :p i have scars
But to be clear, I'd still very much like to enable ECS syncback scenarios in the editor
I also think (but this is more contentious) that we can think outside of the box a bit, and not necessarily reimplement what existing engines have already done. Granular overrides are a must-have for Unity, but like I just said, components are already much more granular in ECS. So maybe component-level overriding is good enough
Anyway, for the most part I'm having a very pleasant experience writing the AST for BSN and any problems I've run into so far have been surmountable
Just perhaps in a limited form
So yeah, π from me on BSN's design
Templates-as-themes relies on field-level patching
Not the most important scenario, but one we are interested in
Because you want to be able to only set the r field of a color? π€ Why is that?
Thought: Oh man, I just realized how much nicer templates can make Hanabi's Module API
Think about CSS, its more complicated than that
This is why I abandoned any attempt to make stylesheets - because I was convinced (by cart) that BSN patching / mixins could be substitutable
You could make every patchable property its own component. But I don't think that constraint is enforceable or desirable
But that means that the editor can't flatten mixins and serialize the merged result
Or to be precise: an editor which did flatten mixins would be useful for a small subset of users. It's why sites like amazon.com killed off dreamweaver: in the dot-com era DW was a big deal, but it was only useful for editing the surface representation of an HTML page, but soon the web evolved and most sites were dynamic, so what you really wanted to edit was the deep structure, of which the surface was only a derivation.
Yeah ok that's fair. I've implemented a CSS-like styling module on top of Flecs once, and that's definitely a different way to model things. I don't think I would want to make that the foundation of my game assets though. Basically what you're saying is that you want to build the entire engine around a model that operates on field-level granularity. That's going to have consequences for performance
(and maybe ECS isn't the right fit for that)
Well, you need to be able to cache entire resolved scenes, otherwise it'll be slow no matter what
Field level granularity would probably like Field Projections from Rust, hopefully they cook
Caching implies cache invalidation, which implies rebuilding caches, which for large scenes is going to be significant. It moves the problem, and for dynamic scenes that you're making frequent edits to I don't think it's something that you can easily amortize.
Could be wrong though
Since it kind of relates to the current conversation, I might as well open up this can of worms I have been saving, which is my objection to this statement from last month:
My assertion is that abstract "things" should (generally) not exist without a runtime representation.
To me this is a very odd constraint to adopt, both from a philosophical and from a practical standpoint. Philosophically, "abstract" and "concrete" are antonyms, and there's nothing that says that they have to share the same structure or shape. Practically, I think this decision weakens the power of abstraction. I can think of a number of examples in which an abstract template in, say, React, produces a DOM which is structured nothing like it; prime examples are<ThemeProvider/>,<ReduxContext/>and so on.
It relates to the editor discussion because I want to be able to preserve in the edited artifact these abstract template invocations
Part of the expressive power of BSN that I've been able to use, even with the limited functionality of the branch, is the ability to "go meta" in the template.
(fwiw flecs also has inheritance caches but at the component level, and it was already fiendishly difficult to keep the performance and memory cost under control)
In the react world you often see code like this:
<ThemeProvider>
<FooProvider>
<BarProvider>
<div etc...>
</BarProvider>
</FooProvider>
</ThemeProvider>
Where the output of all this is just <div...>, but a div rendered with contextual knowledge of the various providers - the point is that none of the providers actually render a dom node of their own, there's no concrete representation. This works because there are two hierarchies: a creation hierarchy and a runtime hierarchy. They have similar shapes but not the same shape.
using skein i often times create such "constructor" components aswell. one existing example is the ColliderConstructor from avian. i think thats a very reasonable thing to do and i think cart has already agreed somewhere in this way too long chain of discssions
Also, not to make this more complicated than it needs to be but:
bsn being so heavily reflection based means that sooner or later there will be a path to execute arbitrary code through the bsn files (intentionally or unintentionally). This being unexpected behaviour from the player and maybe even the game developer opens it up as an attack surface. It might be worth considering (probably in the future) to implement signing of the bsn files for release builds so they cant easily be manipulated. The attack surface is certainly not big and there is room for discussion im just throwing my 2 cents in as someone working in cybersec
The editor not literally crashing over a git-merged scene will be a pretty huge advantage yeah.
(Yes, Unity has done that to me on multiple occasions)
Unreal generally has a solution for this too with "one file per actor" so you don't have merge conflicts in a scene
Looks like Zed devs confirmed my fear and they only fixed the highlighting issue specifically for bsn π«€
The real issue affects all rust proc macros. The real issue is that a punct before a string literal breaks syntax highlighting. Rust allows macros with punct next to string literals with no space
Essentially why I pushed focuse to everything else.
Once I understood Carts vision is just seemed like a waste of effort/resources to produce one not based on bsn.
heya, I was involved in this fix. The context here is that zed is basically re-parsing macro contents as regular rust code, but has carve-outs for specific macros to not do that, such as view!, etc. bsn!, being similar to rust but not actually rust (and more like view! in that way), invalidates those assumptions. So the short-term fix is to not parse bsn! as regular rust code which fixes the broken string issue, the specific long-term is for bsn! to have its own tree-sitter that is aware of its special name constructs (like :variable, :"string", etc), and a general long-term fix for all macros would be like you say and require rewriting the support for highlighting inside of macros as far as I can tell.
The token stream for Rust also does not seem to allow differentiating : "something" from :"something", and the tree-sitter cst is the same for both scenarios.
For the general case, it would be very helpful to have more examples of macros that use string prefixes and a different issue filed for the general case. The team doesn't seem to have a list of relevant, real-world macros to test this against at the moment. In the meantime, a fix for bsn was merged since it looks like bsn is landing in 0.19 and wouldn't want to wait for more general reworks to be done.
That's fine for bsn! to be special cased, especially with future plans to specialize the highlighting, but I'm not fond of the idea that Zed would need real world macro usage cases to fix general case highlighting, especially when creating any macro highlighting works in every other editor I used.
I think short/long term Zed should treat all non-special-cased generic macro invocations as Rust code just like it does now, but 1) make all (non-{[() punct characters the same color 2) do not break string literals when a punct is next to it and 3) drop the match requirement for <> inside macros
yeah I think we're agreeing here. It should be fixed long-term in the general case, and bsn needed a shorter-term fix.
(Because in a macro < does not need a match, only ([{ do)
Not sure if I should leave a comment about my 3 bullet point list suggestion, or open it as a new issue that can be fixed later on
IMO a new issue makes more sense. The original issue filed was for bsn's issues with it, but a more general case is a different fix. The reason I suggested more real-world examples of where this is failing is that this would help motivate getting it actually worked on. If it's not clear that it appears in code people are actually writing, it will be easy to prioritize other work over it. Currently it seems the only macro that the team is aware of that does this is bsn.
I'm not sure the bullet point list will be applicable as a fix, but feel free to include it since it shows what you expect the result to be.
Should I open it as a bug or a feature request? IMHO it's a bug because it's peculiar to see when creating custom (admittedly experimental) macros, but it doesn't quite fit into their bug submission form
I think it's valid as a bug (the general case is the way it is supposed to work), but would have to have that kind of example set to really fit there.
like: its a bug IMO, the question is really "how important is the bug to fix compared to all other bugs?"
I genuinely think at least having punct next to strings break them might genuinely put some people off from the editor when making custom macros
Admittedly that number might be small, but it works in other text editors fwiw
yep, agreed. I think its worth fixing.
Just added support for defining children to my dynamic-bsn branch. Only lightly tested.
Next up will be adding support for the full range of Rust data types you can use to define components.
I was worried that the fact that you don't need commas to separate components in BSN was going to lead to its not being LALR(1), but so far it's been fine, no shift/reduce conflicts
I would mildly advocate for continuing to use LALRPOP here as we care about perf and you can't really beat LR parsers for this (unlike LL parsers they "know what to do" immediately). I don't feel strongly though.
I believe once I have these data types working I'll be able to migrate all of our use of glXF over to BSN. We don't need inheritance at the moment as glXF never supported it (of course it should be added eventually, but I'm focused on getting something useful first)
Today I'm working on fleshing out the BSN docs. As part of that, I've convinced myself that we should reframe the "scene patching" process as the "scene resolving" process. For example Scene::patch, SceneList::patch_list, PatchContext, and ScenePatchError would become Scene::resolve, SceneList::resolve_list, ResolveContext, and ResolveSceneError.
The rationale is that the "output" of the process in question is a ResolvedScene (which should definitely not be renamed to PatchedScene). Patching is a part of the "scene resolution process" (aka some "things" are patches), but it isn't the whole truth of it.
The reframe allows us to be very tight in our definitions and lifecycles (ex: "step 1: resolve the scene, step 2: apply the resolved scene to an entity") in our types, errors, and docs.
This diff largely encapsulates the change from a types perspective:
I think that's much clearer language!
Yes, thank you. I found the names really confusing.
I also found the distinction between a patch and a patch list confusing and not sure why we have two separate concepts, incidentally. Hopefully the docs clarify this.
They exist because a List<Entity> is functionally different than an Entity, so a List<Scene> / SceneList (which ultimately applies to a collection of entities) is different than a Scene (which is always applied to a single entity)
Same rationale as the bsn! vs bsn_list! distinction
I am reasonably certain we could gloss over that distinction if we really wanted to (aka let bsn! be either a list or a single entity). But that shifts some pretty important semantics to runtime / conventions
Godot gets away with this by saying "scenes always have a root entity" (aka they only have a Scene equivalent)
It doesn't matter to me, I just wanted to understand
But given that we have things like:
fn widget(children: impl SceneList) -> impl Scene {
bsn! {
Node
Children [
{children}
]
}
}
I think the list semantics are valuable
@split harness How did you want to maintain the bookkeeping necessary to establish the bidirectional relation between an AST node and the spawned entity? Put some kind of current_ast_node: Option<Entity> in the PatchContext or something?
If AST nodes are entities it could be a relationship
Right, that's what I was planning
I know this doesn't feel good, but I'd prefer to defer this question a bit longer, as its a reasonably important design decision that I would like to think about
(aka until after the BSN PR is out)
Ok, in the meantime I'll just do something
This is why it's good that I've done this spike, it's the kind of thing you won't think about until you slam into it
Yeah come up with your own opinions about how it should work π
TBH I may not need it right now
Since for the BSN viewer I can just blow away all the spawned nodes in the entire world and recreate them when I detect an edit to the file
No fine grained bookkeeping needed
Obviously for the editor that won't be sufficient though
Arenas ftw!
I was thinking the BSN viewer will probably start out of tree, because I need to be able to link third party things (Hanabi in particular, but also probably Avian) into it
Which means it needs to wait until 0.19 to be useful since we'll need Hanabi and Avian to be updated, though I can start on it now
For my mini-scripting-language AST, I used bumpalo, it's nice because you can just scatter rust references everywhere in your structs without worrying about lifetimes
Yeah, I'm going to have AST nodes as entities in the ECS
@white lichen felt strongly about this and I see no good reason not to do it
Arenas only make sense if you are going to dispose things all at once instead of editing incrementally
Right, and we want to edit incrementally eventually
But for cases where you aren't editing, only constructing, they are way less hassle than entities
For now we'll just blow everything away and regenerate. Most inefficient editor ever π
But have to walk before we can run and an inefficient editor is leagues better than no editor
None of this try_get accessor nonsense π
I intend that the live updating BSN viewer could evolve into the editor eventually
I think that naming will be critical - the "AST ECS" is not the same as the "target ECS", and people may get confused
The editor could start as literally just an AST syntax editor sidebar with a live updating preview pane with camera controls/an infinite grid
Picking won't work in that case but again, have to walk before we can run
Picking changes the editor selection range π
Right, you need the bidirectional map in that case tho
SourceLocation { start_line, start_col, end_line, end_col } component
Oh, I was thinking more of an AST node and by "AST syntax editor" I mean something AST based rather than text based. Reflecting over the AST to generate a UI
Kinda like Scratch
I don't think the specifics of the UI matter too much as long as undo is supported from the beginning (not supporting that from the start would be a major blunder)
I've had trouble getting traction on discussing a common undo framework: #1476701973863469230 message
I'd like to avoid having two separate undo libraries (one for the editor and one for people who just want to use the text input widget in their games and apps)
There's no technical reason why these can't be unified, it's pretty easy to generalize without risk of overfactoring
I'm also on the market for better ScenePatch / SceneListPatch names. What they really are is owned assets holding an owned Box<dyn Scene> / Box<dyn SceneList> (respectively), which also holds a list of their dependencies (to allow the asset system to manage the load state) and (currently) also potentially holds the final resolved scene (although with assets-as-entities I'd like to move that to a separate component inserted on the asset after it is fully resolved)
I consider that naming lower priority at the moment though, as those asset types are abstracted away for the majority of users
Yeah, it's similar to Reflect, the guts are complicated but thankfully few have to interact with it
Just pushed the "patch phase" -> "resolve phase" reframing:
https://github.com/cart/bevy/commit/3f893ed65d020df95dd646fa56ccbfeb089835a6
SceneComposition?
Not really sure I understand what is being described
I think we're missing a few Reflect traits that are necessary for proper dynamic BSN. We need ReflectRelationship (or maybe more features on ReflectComponent) and also ReflectFrom (or whatever) to handle the "asset://foo.png".into::<Handle<Image>>() magic.
For now I'm just hardcoding the Children relationship and adding magic logic that autoconverts a string to a Handle<T>.
^ @slender lion fresh issue
(though admittedly this is becoming only tangentially related to bevy at this point)
yes I think we should move conversation to the issue and out of the bsn channel at this point
We should discuss how to handle the bevy_scene vs bevy_scene2 situation.
- Having two "scene" things in-tree together is extremely confusing (multiple scene crates, multiple
Scenetypes, etc). This will make things like Rust Analyzer / IDE suggestions way worse. But also docs / learnability! - We cannot yet deprecate
bevy_scenein its current form, asbevy_scene2is not yet usable for "whole world serialization" scenarios. - The current
bevy_sceneis more likebevy_ecs_serialize, especially when compared tobevy_scene2.
I propose the following renames (to happen after the initial BSN pr, but before 0.19, in the interest of review clarity):
bevy_scene -> bevy_ecs_serialize
bevy_scene2 -> bevy_scene
Additionally, all of the existing "scene" terminology in bevy_ecs_serialize (ex: bevy_scene::Scene) should be renamed in favor of "serialization" terminology. Given that bevy_scene::Scene is literally just a World wrapper type, we could probably just frame this as operations directly on World.
This would be a discrete, breaking change. But I believe it is far better than trying to have overlapping type names in tree for an unknown number of releases (there is no telling if/when bevy_scene2 will be suitable to replace every bevy_scene scenario)
Given how underused bevy_scene is currently (given the lack of official editor workflows), I think this is acceptable
I can't figure out how to proceed without eliminating use of downcast-rs in ErasedTemplate. The problem is that I have to wrap a Box<dyn PartialReflect> in a Box<dyn ErasedTemplate> but PartialReflect doesn't impl Downcast.
IOW I think the problem is downcast-rs has one notion of downcasting while bevy_reflect has another. BSN is using the former but I have to hook it up to the latter.
Also I'm currently hardwiring the only supported templates to be clonable defaultable things. We need a ReflectTemplate to go beyond that. I'll definitely support Handles somehow though.
bevy_ecs_serde? It deserializes too, no?
bevy_scene -> bevy_legascene
I'm trying to switch the downcasting approach to just use Any as PartialReflect is compatible with that, unlike downcast-rs.
Again, I'm really glad that I'm doing this stuff as it would be bad to have shipped BSN with choices that prevent the dynamic implementation from working (unless I'm missing something)
Yes, but calling it serde would imply it uses the serde crate as its primary serialization mechanism, which it does not
Just pushed a first wave of documention and some minor improvements to error handling / robustness:
https://github.com/cart/bevy/commit/e56daf44b1f952fe0a2b36c723fdc5a04a73a40c
(edit: wrong link)
Hmm I see serde as more of a verb then a noun, but I could see the confusion.
We should be sure to make sure both can be feature flagged
OK, my dynamic-bsn branch now has support for struct and enum components, as well as arbitrary hierarchies of templates (i.e. children). Next up is support for handles.
could also have bevy_scene2 be bevy_bsn. I don't feel strongly tho
Or since the old Scene contains a World, how about something like bevy_snowglobe or bevy_microcosm
Could just call it what it is..
bevy_scene_notation
It clearly differentiates it and doesnt complicate the name.
Its BSN.
Actually adding support for handles is quite complicated because I realized I need to implement a ReflectGetTemplate to do this properly. I'm putting this all aside for now until BSN gets a bit more mature.
In my mind, bsn is functionally "just" a format that implements the "bevy scene system"
Ex: we will likely have a binary format that is not the BSN format
We could theoretically have a jsn format that also implements the "bevy scene system" / some third party might do that
In my old branch, bevy_bsn was a separate crate that just had the AST
I mean it's just an AST, you can have binary encodings of that
I don't like this (when paired with bevy_scene) because it implies a connection and compatibility that is not there
My branch isn't too far from having usable dynamic BSN. I think I'm going to leave it aside because I don't really have time to add a whole bunch of reflection infrastructure right now. I could add a hack for Handle but there's no point in adding more hacks if glXF already works.
I strongly believe that bevy_scene needs to go (as-in change to a different name). Its the first place people will look for "bevy scene stuff" and it is not where the vast majority of people should be looking
.scn.ron should maybe be deprecated. People keep trying to use it and then discovering assets/handles don't work, which makes it functionally useless
Isn't .scn.ron just another name for .scn that plays nicer with RON syntax highlighting?
I mean .scn in general π
Gotcha. I do think that it should be removed ultimately, but its filling a role that is valuable to some people iirc
Component serialization is useful for things like the BRP but serializing an entire scene at once to disk in .scn/.scn.ron format in the hopes of loading it later is rarely useful, because it only really works if you have no assets in the scene, which is ~never true for real games
"world dumping and reloading" (with configurable filters)
I do agree / I don't think it can be expected to work for everything. But I think the "filtered" / carefully curated collection of components and entities scenario has merit / usability
But yeah I think bevy_scene in its current form is a niche that most people shouldn't care about
If you throw a ReflectSerializerProcessor/ReflectDeserializerProcessor on top of it to process Handles properly, then it is useful and I think most useful community scene formats are built on top of it
It still doesn't have any solution for prefabs/overrides, but it's fine for what it is
And that niche has very little to do with "scenes" as people conceptualize them in modern engines. Which is why I think a rename is prudent
Incidentally, maybe you've already changed this in your branch but one of the things I found really confusing was the name Scene to mean (AFAICT) "a fragment of the tree"
The word "scene" implies something big and broad
but single entities are Scenes, the way it is now
Maybe Collection or something would be less confusing, IDK
I know it's not a tree which is why "subtree" would be inaccurate, but it would give a clearer mental picture
is the the main/only draw back of bevy_scene ? Will it get fixed after assets as entities?
bevy_scene also has no support for prefabs/overrides which is necessary in a modern game engine
The other relatively-minor-but-still-annoying issue is that anything based on component de/serialization works well on POD components and works much less well on non-POD components (e.g. Avian colliders). My app has tons of bespoke components with names like ColliderProxy that exist only to provide a POD interface that can be deserialized to the real un-serializable components like Collider
There are a lot of Bevy crates, both first- and third-party, that have components with comments like // #[derive(Serialize, Deserialize)] FIXME make serializable after figuring out some way to serialize field 'wibble'
what's a Pod component?
Plain Old Data
this is also true if you want a serialized format of bsn?
Templates formalize the notion of a "proxy" and allow you to create them in a principled way instead of sprinkling a million add hooks everywhere.
I don't have time to hack on BSN right now so I'm going back to shepherding my rendering patches. It's not like this stuff will realistically be usable before 0.19 so I've got plenty of time.
@split harness I would recommend checking what I did with ErasedTemplate to switch from downcast-rs to core::any::Any and PartialReflect because I can't think of any way to implement dynamic BSN the way you wrote it. It's not a big change.
I'd say I've done a nice PoC of dynamic BSN with the noticeable exception of the fact that I assume the template of an object is the object itself, which is not true for Handles and requires a ReflectGetTemplate to fix, but should be doable without rewriting too much.
This is in a current PR
does this help with relationships?
ComponentInfo::relationship_accessor
Stores metadata for a type of component or resource stored in a specific World.
jackdaw might be that third party π
Been going through the tests to see the breadth of what's initially possible and I'm really, really excited for the way name references can be used within a scene.
specifically across hierarchies.
I've been looking at BSN and some of the recent experiments, as well as the chats and am wondering if BSN (and the macro) is primarily intended for composition/UI.
It does seem to me that way, if so I think a fitting name would be bevy_compose.
I am also a bit worried that serialization isn't a priority. Isn't the core of a scene system like this loading/changing/saving scenes?
In theory it sounds great, don't get me wrong, but I think using a macro for composition is already quite controversial and light years away from something like tsx when it comes to the actual dev-ex imo. Declaring full scene hierarchies + "effects" + observers with it seems a bit scary and like it would require recompilation for changes, not to mention numerous known issues/bugs when working with macros.
Am I missing something? I would love to understand but right now I am struggling to get the value other than for the mentioned composition. As others have pointed out I also think the "true" AST is the ECS World itself... that is the final representation of the scene. BSN feels like a layer of indirection that moves us away from that core.
I think with the reflection ground work that has been done and BRP, using the ecs as core makes total sense and honestly you could make a pretty strong argument for how the macro bsn approach conflicts with the ecs philosophy but that's not my goal here, I'm looking at this from a usage and architectural perspective and both feel off to me personally.
If it is mainly about composition (from rust), I might've just misunderstood and I appreciate the work either way just to be perfectly clear. I would think of it then more like rust macro dsl for mainly composition and UI.
I can't answer much of this authoritatively but scene/prefab is the language used in other game engines, and while this is currently a macro its development is laying the groundwork for the .bsn on-disk asset format (which pcwalton and others are building prototypes of for their own teams now the semantics and mechanics are pretty solidly nailed down)
Yea I don't see how that connects then, as others mentioned I think that's putting another layer between the "core" and format/transformation (e.g. a mapper) for no reason but maybe I am missing something.
I don't think that the prefabs do that in the other engines but I would have to check what exactly they do. From what I understand they are snapshots.
Not quite. They're more like dependency graphs, especially in something like Godot.
I think I'm struggling to see exactly where the conflict you're observing is?
I'm trying to clarify my understanding mostly while suggesting a name that seems more accurate to me (currently)
I don't think that it's a bad thing or anything
Right ^^
I just don't see how it would help me other than for composition in a few places in practice
that's mainly it
and from an architectural standpoint, I think it has been explained before. To me the ecs is the core, so it's the only "dependency" that is touched by all layers, it's the domain language and protocol essentially for anything that happens, so it's the source of truth or at least the model that holds it.
does that make sense?
Others have explained it before as well I think
I'm pretty much talking about the same thing more or less
Are you reckoning about source of truth in an eventual editor or in the context of bsn being deployed within an ecs in an editorless world?
Well you're kinda making my point for me, none of these have to know about each other in an ideal world.
just the ecs
but yea in this context especially
I mean, the ECS is something bsn happens to more than anything.
bsn is like a blueprint to create ecs stuff
yeah
I think my understanding of the knowledge graph here is that The Editor knows about BSN, and BSN knows about Component Types + specific named functions registered for the purpose of being used in bsn (like px). But the ECS doesn't know about BSN, or the Editor. and BSN doesn't know about The Editor either.
I think compose makes plenty of sense, but it doesn't line up with artist's and game developer's expectations.
but bsn doesn't either with mine (as a game dev and artist)
for a scene format that is
(I don't intend to come off condescending I think we're all trying to ingest what's going on now it's all coming together :') but I apologise if I have been )
is there a specific use case or foundation that doesn't line up with expectations?
disk assets or hot-reloading in-code ones?
I get the mental model of that but I'm not understanding what bsn does when it sits between the editor and the ecs. Is that when we can serialize (or create a snapshot or prefabs or whatever it is) and save/load a scene for example or do you mean that that already works?
I think disk
it does line up with expectations as a pure rust dev π
but yea that's besides the point I think
Honestly I really recommend that you try to build a game or simple demo using .scn/.scn.ron. Like including colliders, VFX, and so forth. You see what the problems are with just using the ECS really quickly
I ran into some of them with avian and have some problems with hybrid procedural assets where I'm a bit hesitant to implement alternatives currently
I'm not saying these problems don't exist
My understanding, which could be wrong, is that when you create a scene with a single component Velocity(Vec2) that impls Default and GetTemplate you get a scene file like:
Velocity
and when you adjust the x field of the Velocity in-editor to i.e. 42.0 you get
Velocity { x: 42.0 }
and then you "link a dependency" of another bsn file my_bullet.bsn (I can't find a reference to current import syntax atm)
Velocity { x: 42.0 }
Children [ @"my_bullet.bsn" ]
Then we load that, and our bsn file resolves into an entity with a velocity + a bullet child. We could then have a button in our game that re-serializes that as a bsn file but it might look like:
Velocity { x: 42.0, y: 0.0 }
Transform2d { ... }
Children [
Transform2d { ... }
AssetReferenceIdk("Bullet.png")
BulletMarkerType
Children [ ParticleEffectTarget(...) ]
...
]
Which would not be kosher for the intended editor workflow. But on a technical level would be possible, just messy.
that is, bsn isn't a serialize-to format (though it technically could be) but a serialize-from format that is optimised for "patches" on data. Again I'm kinda struggling on what to say cos I'm kinda half asleep and still not entirely sure about what to explain myself.
Thanks, appreciate it, with a file format it starts to make sense to me. That sounds very interesting
So "the editor" operates on bsn, but also "the editor" is just a computer program and other functionality can and will be bolted onto it as needed / discussed / etc.
Like Collider isn't Reflect in Avian so a generic editor can't usefully edit it. Instead you have to use ColliderConstructor which comes with a system that converts ColliderConstructor to Collider. So a hypothetical ECS based editor wouldn't know about the link between ColliderConstructor and Collider.
Templates are just a formalization of the ColliderConstructor pattern, with enough engine-level support that editors can understand it in a generic way. In BSN speak, ColliderConstructor is the template and Collider is the component. With BSN the editor understands the link between ColliderConstructor and Collider and knows "oh, OK, so what I'm editing is the ColliderConstructor and what will actually appear on the entity is the Collider". And it can do that without having to recompile the editor to understand Avian, because that info is all in the reflection metadata which it can slurp from the BRP just like Skein does.
Thanks, yea I'm aware of the problem. This also a pretty big problem for procedural assets that get placed all over the map that consist of many colliders.
(the re-serialization of Node types in particular would be very rough, I just realised π
)
I'm all for a file format like that, just to be clear again. This is my expectation pretty much from a system like that. The macro is also cool but I think it shines in other areas personally.
If I were designing BSN I would have started with the file format and done the macro later, but I understand why cart went the other way.
Anyway, I'm doing the file format now, so no big deal π
The macro is cool but you can make a game without it (constructing Unity GameObjects in code is pretty verbose and people have shipped a million games with it). The file format though is necessary to ship anything that isn't an extremely procedural game since it's the data model of the editor.
But this is a minor gripe and I'm solving it, so
It is cool that you can just copy paste your scene file into code and vice versa, don't get me wrong.
(we'll most likely end up changing to having a general reflectable Collider API component that "describes" the collider's shape, and then a separate non-reflectable ComputedCollider or similar that has scale and preprocessing etc. baked in and is used for the actual collision detection stuff, but yeah the point still stands and is accurate to the current state of things π)
I imagine you'll throw the whole thing out in favor of Templates when BSN arrives?
Or well, more accurately, you just make ColliderConstructor a Template and keep it around
Hm probably not? But I'll have to look more into templates
I think basically all you have to do is say impl GetTemplate for Collider { type Template = ColliderConstructor; }
and then impl Template for ColliderConstructor { fn apply(...) { ... build the collider ... } }
Doing it that way means the editor becomes aware of the link between colliders and collider constructors
not sure if Collider should be assets, I remember in rapier Collider are internally Arc-ed, so performance is better?
The gist is that right now Collider stores both an unscaled and scaled version of the given collider, and then the reflectable ColliderConstructor is a separate thing that only constructs a collider once, intended for skein-like workflows. You can't change a ColliderConstructor at runtime and have it affect the Collider. So the "unscaled" shape stored in Collider is the actual source of truth, it can just be instantiated with ColliderConstructor
The proposed change I've been wanting to experiment with is removing ColliderConstructor, but splitting apart Collider such that the unscaled shape is the new Collider, which is reflectable, and then that is used to construct and update the ComputedCollider, which includes scale and whatever preprocessing needs to be done on top of the raw geometric description in Collider
The eventual plan there would be that we can upstream Collider as the general physics engine agnostic collider API, and then each physics engine can implement their own internal collider type that is computed and updated based on the Collider
That is exactly how Template works π
Ultimately Collider needs to be the source of truth that you can modify at runtime and have it affect the downstream "computed" colliders; it's somewhat unclear to me if this can also be a Template or if templates are an instantiation-time kind of thing
But I think it could be a Template, yeah
They are internally Arc-ed yeah, but we'll switch Parry to Peck eventually when it's ready, and also Bevy will want an upstream Collider that doesn't depend on any specific physics engine or collision detection library. So we should focus more on what's best for Bevy in an idealized world, and not about what specific existing engines do
(Arcs could still be best, but it may be useful trying out other approaches too)
Genuinely I am so excited for this though, even if I'm still hand-authoring for the time being. I want to see if you can construct
#A
Ref(#D)
Children [
#B
Children [
#C
Children [
#D
Ref(#A)
]
]
]
in-tests (it seems to be the version of this that's dodged atm), but that you can just easily express these kinds of connections is incredible.
Edit: yeah this just works fine. Really happy to see.
Yeah, I've been wondering why we're doing the macro first since afaik it doesn't really meaningfully unblock any editor work and overall feels more controversial π€ If I had to guess, it's because the focus was more heavily on making UI nicer, and also exploring reactivity and other things that don't work as naturally with scene files
the timeline also obviously wasn't meant to be nearly as long as it has been
either way, happy to see you working on the file format, exciting stuff π
I think the reason was just that it's easier to do as a macro to define syntax and such
the asset format would also require versioning which feels very complicated
well, I'm skipping that for now π
LALRPOP has actually been quite simple
Writing parsers in rust is pretty easy. Source: Our team wrote our own level description language, and the parser was done basically instantly compared to the rest.
OK, my dynamic-bsn branch now has the ability to reflect templates with ReflectTemplate/ReflectGetTemplate and so you can create templates that aren't identical to the original components and it all works.
Next thing will be to add a hack that adds the magic Into behavior for HandleTemplate, so you can write asset paths as strings. The right solution would be something like ReflectConvert but that would be scope creep.
Rust macros are quite open-ended with possible syntaxes, but still have a few hard rules here and there for what will and won't work in a macro. Designing the macro first ensures no surprises later on if designing the file format first and then trying to squeeze that into a macro later.
And bevy having an inline bsn! like how Yew has inline html! is incredibly cool and useful for quick inlining without having to load assets.
I think doing the macro first was the right play, with the only real downside being if a certain feature can't be supported but exists in the macro.
this is a really helpful example for understanding that would be nice in the release notes
I still think Scene is the right name. This is still a valid Scene, even though it is "just" a single piece:
fn foo() -> impl Scene {
bsn! {
Position { x: 10. }
}
}
In this case it is a TemplatePatch<Position> which impls Scene.
It interacts with the rest of the scene system in the same way as a more complicated "full" example would (or a "big / broad" scene as you put it), and it can be used in all of the same places
fn level() -> -> impl Scene {
bsn! {
:foo
LevelRoot
Children [
/* pretend this has many children */
]
}
}
This type of recursive compositional typing is also just cleaner/simpler for accomplishing this goal. Introducing a second trait for "single" pieces would cause discontinuities in the type system and make it all less usable. We could call the single unified trait SceneFragment or something and defining scenes in code would look like this:
fn foo() -> impl SceneFragment {
bsn! {
Position { x: 10. }
}
}
But from my perspective this is wrong. It is returning valid semanatic BSN (whether it is a single "piece" or multiple). Bevy scenes are conceptually compositional in the same way as the trait. We don't call them .bsnf files (Bevy Scene Fragment files), and I think introducing that naming would be unhelpful / confusing / overly jargon-ey.
Cool yeah I'm not opposed to dropping downcast-rs if Any can do the job / that would be better anyway
I'm already Godot brained enough to see tscn "scenes" for everything. Having bevy also call them "scenes" is fine as far as I go
I am also a bit worried that serialization isn't a priority. Isn't the core of a scene system like this loading/changing/saving scenes?
If it is mainly about composition (from rust), I might've just misunderstood and I appreciate the work either way just to be perfectly clear. I would think of it then more like rust macro dsl for mainly composition and UI.
It is built with scene files / serialization in mind. I encourage you to read the design docs and draft PR: https://github.com/bevyengine/bevy/issues/23030#issue-3955341425.
Most of the groundwork has been laid / the in memory asset system is already present. The only missing piece is the actual serialization / deserialization bit, which is comparatively straightforward.
FWIW the asset format and the macro were initially designed and implemented in parallel, in the interest of proving out the concepts. During the latest rearchitecture I prioritized the macro / in memory bits as an optimization because that is where most of the unknowns and complexity were, and that is what things like Bevy Feathers will use / what we'll use to build the editor UI
As I mentioned, I already have a recursive descent BSN asset format deserializer / AssetLoader for an earlier version of BSN. It has just diverged enough from the latest version that starting over is more productive
Just pushed support for handles to my dynamic-bsn branch
Templates can be rebuilt at runtime (by calling template.build_template(context)). This requires a TemplateContext, which can be constructed from an EntityWorldMut. Currently there isn't a helper to make that easier (ex: let collider = entity_mut.build_template(ColliderTemplate::default())?;), but we can/should add that eventually.
But if the Goal is to ergonomically mutate something at runtime, it is likely best to do that work on the output of the Template (ex: the Collider), unless you benefit from / need the access to World / the current entity context.
Can you send link to your branch?
Should just need a few more types (floats, vectors) and a sprinkling of #[reflect(Template)]/#[reflect(GetTemplate)] and then .bsn assets should be mostly usable
and code cleanup, lots of code cleanup, it's a mess
(And my branch contains the missing serialization/deserialization bits, mostly)
Thanks
Warning, super ugly
I also think itβs a bit confusing to name the entire thing a scene, but then also each individual part of that scene a scene. I get how itβs technically correct in that it could be a scene, but IMO scene implies intent by the creator as something thatβs intended to be experienced
Yeah I do agree that this aspect is a bit muddled as a result / there is a tradeoff there
"Intent to be experienced" can be encoded in other ways:
- Storing a scene in an asset file
- Assigning the scene to a type (ex: the
bsn!{ Button("My Button") }that both @rare whale and I want)
But there are also benefits. When "everything is a scene regardless of whether it is intended to be a 'piece' or a 'whole'", then you can use scene assets for "patch / style" scenarios freely. If we arbitrarily limited the scene asset file to being a "whole" , then it wouldn't be usable there.
At the end of the day, we could add that type of restriction later if people don't like it in practice. However I suspect that in general, people will appreciate the "everything is a scene and cross compatible across use cases" aspect
Getting the perfect label is less important than getting a consistent and specific use for the label
Coming from Godot "everything is a scene" is pretty much second nature to me now
(and I think this division accomplishes that)
As Godot is propping itself up to be a MAJOR indie game engine (slay the spire 2 uses it) I think more indie devs will be used to the idea of everything being a scene
@rare whale I think it is time to start prepping a Feathers BSN port, as I think landing them together is valuable. I've done that once before / I could probably bang it out pretty quickly while the BSN PR is in review, but if you want to do that now you'd have a head start on me.
After all, what is a scene but a collection of items? What is a player but a collection of items like weapons, shields, etc. Why differentiate so arbitrarily?
Thanks. I have read it and everything I could find before posting.
I was mostly responding to the discussion about the naming and trying to understand the current state. I think the confusion comes from prioritization.
If it is relatively straight forward then it is low hanging fruit imo and it seems that it is high priority to others - myself included. As I said I still appreciate it, I just wanted clarification π
If you think it can be quick, then feel free. My main issue is ensuring that we don't ship a broken experience for non-BSN users of feathers (the current experience is broken IMO).
I'm ok with using temporary names for the template functions while we sort things out
Ideally before next release
- [ ] Feathers BSN Port: Largely already done. Just need to reconcile with current state of main. This will help BSN land well, so landing it alongside BSN is a high priority.
- [ ]
#Namereferences in more places: The UI eventing scenario really wants#Nameto be usable in closures. This would functionally be expressed as a template that returns a closure that accesses a specific entity. This unlocks a lot of value for UI devs, so ideally it lands alongside BSN. - [ ] Top-down vs bottom-up spawn order: Currently BSN follows the normal bevy top-down spawn order. I think we should heavily consider spawning bottom-up, in the interest of making scene contents available to "higher level" components in their lifecycle events (ex: a
Playercomponent accessing nested entities like "equipment" when inserted). If we decide to keep things as they are, we probably want to introduce additional "scene ready" entity events that trigger "bottom up". - [ ] Struct-style inheritance: It would be nice to be able to do something like
:Button { prop }instead of:button(prop). I'd really like us to explore this being component-tied (ex: associate a scene with a Button component). - [ ] ResolvedScene-as-dynamic-bundle: ResolvedScene should insert all of the components at once as a single bundle, rather than one-by-one, which is really bad from an archetype move perspective.
- [ ] Inline field value expressions: Support cases such as `px(10).all()
- [ ]
derive(GetTemplate)generics ergonomics
Some of those (such as ResolvedScene-as-dynamic-bundle) are reasonable chunks of work / very unlikely to land in time
Given that BSN is already barely squeezing in, its very possible that none of these will land π
Let me go over the issues I am concerned with the non-BSN API for feathers:
- Originally I had thought to allow users to pass in a bundle of "override" components that would be patched in, allowing them to do things like replace specific components (SliderRange etc.) in the same way that BSN does - turns out that this doesn't work, and I removed the
overridesparameter in a later PR but I missed a few places that were on a different branch. Currently there's no good solution, but I have talked about this in ecs-dev. - In my own branches, I have several "container" components (menu button, title bar, etc.) which only have BSN APIs, because I'm not sure how the non-BSN API should work. In the case of panel or title bar, it's about the widget being able to decide where the children should go; in the case of menu it's about the children being created lazily (we don't want menu items hanging around, invisible, when the menu isn't open).
I'd really like us to explore this being component-tied
As you know, I disagree with this for reasons I've explained in this post: #1264881140007702558 message
Yup. I think this is controversial enough (and depending on the route we land on, implementation-heavy enough) that it probably won't land in 0.19
My personal take is that this all points to Feathers widgets being BSN-only.
But I'm happy to keep the bundle functions around / try to support what we can
Can we take the pulse of the community on this? I'm fine so long as nobody is screwed by this.
For sure. Also not something we need to sort out for 0.19
re: Top-down vs bottom-up spawn order: the bevy_editor_prototypes proto_bsn version uses bottom-up and it's very intuitive to work with. I do agree that the "scene ready" event is crucial to have there, but it's probably a must have for top-down as well, I'd figure.
Unless we hit a wall in the port that requires giving up the bundles
In my own branches, I have several "container" components (menu button, title bar, etc.) which only have BSN APIs
Given that these aren't currently onmain, we don't need to land them in 0.19
Although it would be nice to
Assuming I can even remember where they all are; It will take a while to hunt down all the various branches. In addition to widgets in various bevy branches, I also have prototypes in bevy_reactor for BSN templates of node graphs, reflection property grid, and gizmoids.
FWIW my current thought is that I'll switch my project to BSN when 0.19 comes out, maintaining my branch as a backport to 0.19 until it lands.
cart/next-gen-scenes ( this pr ) has some of them
And those are already ported
OK I see menu.rs in that branch, and I found in another branch flex_spacer, pane, and subpane. I need to find where I put listview.
I posted a proposal for how to develop the editor alongside BSN features in #editor-dev
I've noticed one common failure mode is forgetting to say #[reflect(GetTemplate)] when you write #[derive(GetTemplate)] on a component that has handles in it. You end up with a confusing error message as the deserializer assumes that, because the type didn't have a custom GetTemplate implementation, that must mean that it implements Default and Clone, which it doesn't because it has handles, and so you get an error message mentioning Default when the error has nothing to do with that. Probably worth thinking about how to improve this error message.
Got this dynamic BSN file working: https://github.com/pcwalton/bevy/blob/dynamic-bsn/assets/scenes/example.bsn
I need to write a custom lexer now to get the shadows working because LALRPOP's default lexer thinks true/false are identifiers π But other than that it works great!
Oh my goodness it's really coming together
Missing features are:
- bool literals
- int literals
@(TBH I don't even fully understand what it does):inheritance is untested, it should work in theory but you know how untested things usually are- comments
Notice what is supported: #Name, Default, unit variants, unit structs, tuple-like variants, tuple-like structs, Struct { ... }, float literals, Children relations, arbitrary GetTemplate/Templates that don't have to match the type, and a magic hack to allow the string literal -> AssetPath conversion so that handles work (this should become something more principled eventually)
@split harness Just as a heads up, if you allow putting arbitrary things in {} then I think the grammar might not be LR(1) anymore. Because you can't tell whether Foo . { foo : is supposed to shift or reduce (to a unit struct Foo) without seeing the : and that's 3 tokens of lookahead.
And if you allow arbitrary things to be in () it's straight up ambiguous because Foo(Bar) could be an enum variant Foo(Bar) or two unit enum variants, Foo and Bar (the second one needlessly parenthesized)
Oh, also if you let things be put in {} that's actually technically straight up ambiguous, not just not LR(1), because {} is mostly a synonym for () in Rust and so Foo{} is ambiguous between a struct Foo {} and a unit enum variant Foo followed by a template which is () (though () isn't a valid Template so you could just rule that out at the grammar level)
The LR(1) problem is seriously annoying though. I'm not sure if I can factor the grammar to try to eliminate it. As a worst case I could implement a lexer hack with 3 tokens of lookahead. Not being LL(1) or LR(1) kind of sucks though for tooling...
@split harness Yeah, I don't know if I can make this LR(1). I hate to suggest this but I would recommend changing the BSN grammar because it's too hard to parse right now.
Not being LL(1) means you can't write a handwritten recursive descent parser and that's just bad. You're saved by the fact that macro rules is an Earley parser or something, but that's O(n^3)
Actually hmm, it looks like you're LL(1) right now because you have a handwritten parser? I need to study your parser more closely
is there a Rust implementation of TreeSitter or Lezer? Their grammar is very powerful. I am not sure why we must force the grammar to be lr(1) or ll(1). This seems limit the expressiveness of the grammar.
I'm not interested in debating which parsing algorithm is the best. The point is that people will want to write LL(1) parsers because those are the simplest to write
@split harness Oh, I just noticed you're using capitalization as part of your parsing to try to detect whether parts of a path are modules or variants or constants? I really object to this π
I mean, maybe if it's absolutely necessary, but there should be some way to override it
I donβt think arbitrary () or {} are allowed at the top level, right? I think they always denote opening a new βpatch scopeβ for the template type before it (or they should)
It seems to be allowed at the top level
BsnEntry::Parse has an if input.peek(Brace) { ... }
I think bare {...} is for embedding arbitrary Rust expressions that the cover grammar doesn't allow for
ah yeah that's not great
I think the easiest fix might be to change { ... } to ({ ... })
since I think { ... } isn't that commonly used?
if I change it to ({ ... }) then LALRPOP reports no more conflicts
what if we just didn't allow that π€
I think we can always inherit rust scene variables from outside, or call fn() -> impl Scene
actually sorry, ({ ... }) doesn't fix it either
Yeah, I prefer either of those solutions tbh
TBH when I saw that BSN had changed to not require any separators between patches my ambiguity spidey sense went off
I have battle scars from this stuff
ugh, it seems to be load bearing and I don't know why
I can't remove the {} in bsn.rs without errors
OK, I can remove all of them in the bsn.rs example but one: changing {sprites} to sprites causes an error because I guess it's looking for an impl Scene but it has a Vec<impl Scene>. IMO if the only thing we're using {foo} for is to embed a Vec there are other ways to do it, like ...sprites. But I also don't understand why it couldn't just be sprites, maybe that's a bug?
In any case I'm not going to implement {foo} in dynamic BSN pending a decision from @split harness.
More broadly, I get why the decision was made to have no separator, because the alternatives are really verbose from what I can see, and except for this {} thing LALRPOP confirms there is no ambiguity, but we're skating on thin ice
I think no seperator so the grammar is not that strict so people can write it easier. But I think maybe consider do it like "js automatic semicolon insertion". So the grammar is a regular & strict one with seperators, then you still allow user write without it
Oh no, no ASI please π
You have no idea how much that constraints the further evolution of the language
Listening to TC39 gripes it's like 80% of the language proposals for JS end up getting immediately shot down because they would cause a backwards incompatibility hazard due to ASI
It's just a mistake
My worry is that if we do need arbitrary expressions we don't have a lot of tasteful options, short of something like $(...) or expr(...)
we should just do like typst does /hj
# all day
Lua has this problem btw, because they have no separators, f()(x()) is ambiguous between f()(x()); and f(); (x()); In this case they actually are whitespace sensitive to break the ambiguity, but this isn't an option because bsn! is a macro and can't modify the Rust lexer
I asked gemini what are these proposals. I think they are mostly syntax for general programming languages, not a markup like language like BSN. TBH I used Scala and TS and find ASI quite fine
(at least I prefer it compared to no seperators
If we have to keep the grammar as is there is a backup lexer hack that I think mostly works. You define two types of {, "struct {" and "other {". When you see { you look ahead 2 tokens. If the next 2 tokens are IDENT ":" or the next 1 token is } it's a struct {. Otherwise it's a other {.
You decide for yourself how tasteful that is. π
But intellectual honesty requires that I mention it
Anyway, none of this is necessary for unblocking the editor (and my project's migration to BSN). I think all I need is a little more work on the lexer, a few more literal types, and updating to cart's current branch, and the editor is unblocked.
If you can think of alternatives here I'm open to them. Verbosity would take a massive hit if we needed to specify struct/enum/const for every type
But for the few cases where the ambiguity comes into play (edit: or people choose to not follow conventions), its worth having a break-glass option
I believe it is necessary if we want something usable
I'll give this some thought once the PR is out / reviewable
Yup I'm aware that this was accomplished on a razor's edge. I'm open to alternative proposals, but I think the current spec is a reasonable solve
I believe the disconnect here when using sprites is that it is interpreted as a Scene parameter for the first entry in the scene list
Its like doing this at the top level:
let transform = bsn! { Transform { position: { x: 10. } }};
bsn! {
transform
}
Or this with children:
let transform = bsn! { Transform { position: { x: 10. } }};
bsn! {
#Root
Children [
transform
]
}
Definitely a bit weird, because these behave differently:
let transform = bsn! { Transform { position: { x: 10. } }};
bsn! {
{transform} // Assumed to be a Scene
}
bsn! {
#Root
Children [
{transform} // Assumed to be a SceneList
]
}
using different syntax for SceneList would re-add the parity
Or alternatively, requiring () for all child / related entities
If its still unclear, currently this:
Children [
transform
]
is interpreted like this:
Children [
(transform)
]
Because we do not require () for children
This is what enables:
Children [
Node { width: px(10) }
]
And
Children [
Node { width: px(10) },
Node { width: px(10) },
]
Throwing one more name idea in the ring: bevy_clutch
Bird jargon concerning groups of eggs
Havenβt seen it used for an existing crate
Maybe a more literal name would be nice? Like just something that describes exactly what it does in 1-2 words?
i like bevy_scene. it'd be where bevy scene (notation) lives π
It sounds like the name is only a question because both the old and new modules and types will coexist, because the new stuff isnβt yet a total replacement for the old stuff (and might never be)
I do think scene is a good name to stick with and fits with how peers (like Godot) use it.
I donβt think scene is a bad name at all, but what do you call the other scene? Youβd have bevy_scene::Scene and bevy_scene::Scene
But you canβt
So one of them has to change
Vignette? Tableau?
rename the old one to UncoolScene, AdvertisementSceneForBsn, or NaiveScene π
ScenePoser
SceneFromBeforeItWasCool
NSFW scenes should be called Obscene
Oh, wait, I bet @ means "look up the Template with this name rather than the GetTemplate"?
Ah yeah, exactly
OK, that's the last thing I need to implement then.
Here's where I ran into it: There is a component called CascadeShadowConfig. It has a fairly low-level API which specifies the distances as raw values and isn't very user-friendly to use, so there's a user-friendly shim on top called the CascadeShadowConfigBuilder. In the editor you should be editing a CascadeShadowConfigBuilder, not a CascadeShadowConfig. This is precisely the Component/Template distinction that people keep reinventing, just implemented yet again in an ad-hoc way. BSN allows us to express this pattern in a less ad-hoc way, and unlike every other scene format proposed for Bevy actually allows the scene format to embed CascadeShadowConfigBuilders.
In fact... maybe most of the examples should be reworked to use dynamic BSN? Because people will want to open them up in the editor!
It's also a stress test for dynamic BSN to make sure it's actually useful for dogfooding
We'll probably want some convenience templates for things like creating cube meshes and simple StandardMaterials for colored cubes and whatnot
Almost definitely yeah, we also now have support for viewing multiple files in bevy examples so even viewing them in-browser is somewhat straightforward.
I've been holding off on adding better docs to examples largely because there will be a bsn reckoning to them.
(also I've been busy, or tired, or otherwise occupied)
OK, I've now fully recreated the load_gltf example using this dynamic BSN file: https://github.com/pcwalton/bevy/blob/dynamic-bsn/assets/scenes/example.bsn
Since the last time, I added @ templates, bool/int literals, and comments.
That's basically it except for tuple struct expressions and use. (I don't feel like use is that important though because editors will probably not have much use for it and I suspect .bsn is going to mostly be edited graphically. Editors should probably avoid use in fact as it tends to cause merge conflicts.)
This is what the code looks like to spawn a scene using a .bsn file, BTW. https://github.com/pcwalton/bevy/blob/dynamic-bsn/examples/scene/dynamic_bsn.rs#L41-L44
I think dynamic BSN should only be used in examples where it is relevant to what is being illustrated. Otherwise bsn! is better at keeping the context in the code / being "illustrative"
Can you reference bsn in another bsn file?
You don't think most folks will want to load examples in the editor? I don't feel strongly here, but am surprised
Should work π
Well, actually, no, not yet, not in the way you're probably thinking. You can't resolve inheritance from nodes not in the local BSN file yet.
Artists might want to e.g. open the clearcoat example so they can get an idea of what it looks like and they will want to see it in the editor, I think.
Though, I suppose the BRP could be an alternative.
Why not have both the example code, which uses bsn! where it makes sense, and then have the .bsn for users to read/use.
Definitely need import syntax π
I'd take a PR to add it but I'm not likely to add it myself. For me dynamic BSN isn't so much about hand editing, as you can do that with the bsn! macro and the experience is better because you get autocomplete. Rather, dynamic BSN is the format I intend to use for machine reading and writing.
Of course I'm not questioning the decision to have use in BSN, just saying I won't be the one to implement it π
I think there are potentially three different dialects of BSN assets:
- "ergonomic" BSN - intended to maximize readability and human authorability
- "normalized" BSN - intended for ease of parsing and machine readability
- "compressed" BSN - intended for optimum loading speed
So "use" would be a feature of ergonomic BSN (to conform with DRY). Compressed BSN would have a string table at the front and reference all identifiers by their index in the table.
I use a bespoke ZipAssetReader for my project. 80% as good as compressed BSN for 1% of the effort π
I thought about that; you still have to pay the cost of computing a hash for those long names after they have been decompressed
But yes, a "real" format for compressed BSN would be good to have
Something like thrift or messagepack
BTW my dynamic-bsn branch has been revised to use the ECS instead of SlotMap. cc @white lichen
All I need to do now is to update to cart's latest branch and I'll be good to start migrating to it in my project in preparation for 0.19.
Also, self-nit: I'm not sure whether "normalized" or "demormalized" is the right term here. The analogy to "normal form" in the RDMS sense is imperfect.
ill take a look
Note that I still throw the temporary World I build away in the AssetLoader because I'm not sure where to put it. Probably needs design input from cart
For a "level 1" editor throwing away the World is fine, but levels 2 and above need the BSN World to be persistent.
@split harness Have you given thought to how to provide patches to glTF files? For example, "instantiate Player.gltf and attach a particle effect to the node named #Sword". Or "instantiate FlightHelmet.gltf and add iridescence to the material of the object named #Goggles"
Having the ability to do that would be such a breath of fresh air compared to the current approach of "have a system that watches the scene for objects with certain names"
I guess you'd have a special AssetLoader for glTF that imports them "as though they were BSN" (i.e. to a ScenePatch)?
Then you could inherit from glTF, because it'd be just another way to get BSN as far as Bevy is concerned.
Anyway I answered my own question. Ugh, I want BSN now π It's a night and day difference in how Bevy games are constructed
At least games that have to work with a lot of art assets
Actually, looking at this again I'm not sure this is possible? At least from reading the code it's not obvious I can switch to targeting a different node in the tree of the thing I'm inheriting from.
This is essential functionality for most games BTW.
Or maybe I can build this with a custom Template? Because it looks like ResolvedScene::apply builds the inherited scene first before running child templates. So maybe I can poke around the EntityWorldMut I got to find the inherited entities and munge them in my template.
Something like a custom struct DescendantOverrides(HashMap<Name, Vec<Box<dyn ErasedTemplate>>);
Or I guess struct DescendantOverride(Name, Vec<Box<dyn ErasedTemplate>>). In the guts of DescendantOverride you search your subtree for an entity with the given Name and apply your templates.
This is something that can be prototyped out of tree but I think an editor is going to want to know about things like DescendantOverride because of the workflow whereby you drag a glTF into the scene, then click the dropdown to find some bone or sub-mesh and change a property on it. You'll want the editor to automatically generate DescendantOverride templates on the root entity. Unless the developer right clicks and says "Unpack" or whatever, at which point the templates of the prefab will get copy pasted into the .bsn file.
I know at least in Unity this workflow is supported, but with limitations: there is no reordering and no deletion of prefab descendants (I think?). It's only additive. It's important that the additive workflow is supported though because of the common workflow whereby artists create entire model subtrees in Maya/Blender and then import them into the game engine where game-specific components are then added.
My dynamic-bsn branch is now updated to cart's latest branch.
I did a spike and I think that I can do something like @DescendantPatch::new("Sword", MyParticleEffect { ... }). This isn't the ideal syntax but I think it works. I'll try it more tomorrow.
I was talking about something like this a while ago.
Users that prefer to compose big scenes in external tools (and number of such users will grow with USD / glTF references) would want to attach components on asset import.
And even users that work with small glTFs will often encounter this too.
For example when importing a car user may want to insert components on doors, wheels, headlights, trunk.
There should be a way to reference assets internal object by name on import and append needed components. And maybe even remove/replace components.
We should also take into account that user may import a file that references sub files.
For example user imports USD / glTF (with external ref support) file that references a couple of files that all contain internal object with the same name "Sword".
User may want to insert component on "Sword" only in sub file A and not B.
To make this work we probably need to allow referencing a file internal object by full name. Something like "A.gltf/Weapons/Sword".
im excited to finally see it ^^
OK, I got this working in a proof of concept branch:
bsn! {
:"scene://ui.bsn"
@DescendantPatch::new("OkButton", Text("Hello world".to_owned()))
}
Where the DescendantPatch call means "find an entity called OkButton in the subtree and apply the given template". The resulting scene changes the text of the OK button to "Hello world".
@split harness I'm curious what you think about the above DescendantPatch when you get a chance. Not sure if I did this in the way you were imagining or not. I imagine you were probably thinking about having some sort of built-in support for this in BSN eventually? In any case it's good that third-party crates seem to be able to do it for now.
If I understand correctly, this would pretty much replace Avian's ad-hoc version of this, which lets you configure collision layers etc. for colliders that are generated for a scene
// Specify configuration for specific meshes by name
commands.spawn((
SceneRoot(scene.clone()),
ColliderConstructorHierarchy::new(ColliderConstructor::TrimeshFromMesh)
.with_constructor_for_name("Tree", ColliderConstructor::ConvexHullFromMesh)
.with_layers_for_name("Tree", CollisionLayers::from_bits(0b0010, 0b1111))
.with_density_for_name("Tree", 2.5),
));
If so, this would be very useful for me
Yes π It's generic and if it were a full version you could specify arbitrary paths.
This is pretty essential functionality for most artist workflows.
You could even imagine some sort of XPath-like syntax.
Cool! Yeah this has been a major pain point with Bevy's scenes IME, so I'd love to see something like this
Could it work for something like propagating VisibilityRange to all entities with a Mesh under the SceneRoot? I guess that's not really a patch and more just adding a component. I don't know if bsn already has tools for that
In this specific case I do think it's something that should probably be built-in but it would be nice if bsn made it easier to do something like that user side
I assume "OKButton" is a #Name?
Yep. Also note that this has a hack that propagates the templates to all children of the entity with that name just for this example. A real version could have something like XPath.
Many of Bevy's current examples exist to illustrate specific APIs, which often consist of both function calls and instantiating types. If we move the instantiation to another file, for many examples this moves important context to a separate place (which is notably fully detached from Rust Analyzer, which for many is an important part of the learning experience)
This should be super straightforward to add, given that the scene system already handles the resolution for you
Yes, but I don't know if BSN is the right place for that
I'll note that inheritance is still extremely useful in the context of editor defined scenes. I used this all the time in Godot to break scenes into logical pieces
Understood. When it comes to templates that create hierarchies, there are two schools of thought - one team (which I prefer) likes the child hierarchies to be encapsulated black boxes with a well-defined surface API; the other team prefers for the hierarchy to be transparent and manipulable from the outside. Having externally visible names is somewhere in between: the child hierarchy gets to decide where the patches go, but not the details of how the patch is applied.
There's definitely some things that could be moved to separate files though, like having maybe slightly more detailed scenes without bloating the actual code with all the spawn boilerplate
I do think that we should start moving "incidental" scene definition out into scene files which get loaded for many of our examples. Sort of the same idea as just calling .add_plugins to grab a camera controller
Currently gltf loads a bevy_scene. I think ultimately this should be bevy_scene2 instead. In the short term, we should have a second loader
Patching children hasn't been implemented yet, but its definitely something we want to do / its on my todo list
Iirc there are pieces that need some changes, but the general patching interface and lifecycle already support this to a degree
Right the big missing piece right now is doing this correctly / in a copy-on-write way for :scene. If you do "immediate" scene merging with scene(), we could support descendant patching without issue
:scene will require additional "smarts"
See my follow-up reply, I got it working π Though maybe not in the way you anticipated
I believe Godot supports Patch / Add / Remove. Definitely the first two
The problem is really artist workflows. Suppose that you have a character and you want to put a particle effect on the character's weapon so their sword makes a trail. The sword is going to be a node in the FBX file that the artist made in Maya. But there's no way in Maya or FBX to say "this is where the particle effect goes", since it's just a modeling tool. So the only way is to pull in the FBX file as a prefab and poke at individual objects within it.
The only real alternative I can think of would be to teach the modeling program about Bevy components specifically, which is the Skein approach. But that's a radical departure from the typical artist workflow and requires that the modeling tool be taught about Bevy. Maybe that's OK if all the artists use Blender but lots of artists (incl. my entire studio) use Maya instead. Maya does support plugins but it's very version specific and there's a proprietary API and ugh, it's just a mess.
Yeah first-class support in BSN is on the agenda / I think it needs scene-system-level features to get us all the way there
As long as the DescendantPatch approach I prototyped there will continue to work as a third-party thing in 0.19, I'll be very happy
We can throw out a ridiculous amount of gross code in my project
This is a "big" issue we'll need to sort out, as #Name is not currently something we record in the ResolvedScene and Name (the component) and the EntityPath that builds on it is a post-spawn result. But patching needs to be resolved pre-spawn. #Name is also "scoped" (the same entity can have multiple #Name, with the "newest" / most specific #Name being the final Name)
Also @rare whale
Yeah, I do the patching post-spawn to work around this.
Pretty straightforward to store #Name in Scene, but it will have overhead
This is not great but it's strictly better than what we do today, which is have an entire system that runs every frame scanning for objects with magic names
And I don't love paying that overhead for every #Name, even if child patching is never used
Sorry descendant patching π
Yeah this can continue to work, but its not an endgame approach
And yeah I think we probably want something XPath-like, because Names aren't globally unique
Or maybe like CSS selectors
Anyway if you could continue to give some thought to how to do this it'd be really appreciated. I'm happy to work around it using post-spawn patching in the meantime for 0.19.
Oh also, note that this isn't the first time we will have something XPath-like in Bevy. Animation targets also work the same way (they target bones with things like Hips/Stomach/Spine/Neck/Head)
@split harness BTW the problem I ran into is that the inherit field on the context stores a handle which just gets instantiated wholesale, so there's no way to insert hooks. I guess what you would want is some sort of list of map from XPath to patch in the context that gets applied as the patching infrastructure descends in the instantiation process?
that's unfortunate to hear maya's plugin apis are a mess. I was hoping we could add some support there at some point. skein's core is the gltf extension for components, so can accept the output from any program (people have experimented with godot and such producing it).
I'm not an expert here but my understanding is that the problems are (1) Maya's API is a messy C++ API and (2) the API is extremely unstable from version to version, so studios get all their artists on a single version (Maya 2020, Maya 2021, Maya 2022, etc.) and upgrading the plugins is an ordeal. The upshot being that you'd have to maintain versions of Skein for every recent yearly release of Maya.
yeah that makes sense, thanks for the context
after the asset format shakes out I'm hoping to break ground on a blender->bsn exporter this year
Exciting! It would certainly make things easier for me if all our artists used Blender, but grizzled industry artists have their workflows and getting them to change is impossible π
absolutely. I'm under no illusions that artists will change their workflows π
But yeah, even having post-spawn DescendantPatch will be an enormous cleanliness win for us.
Really maybe it should be something like
@Override("#OkButton > Text", Text("Hello world"))
Where you give it a CSS selector with child-of, descendant-of, name (ID), and existing component names, and then supply a list of templates to get applied to everything that matches
Oh and @split harness if you had the list of current overrides in the context then you could possibly avoid tracking if there aren't any hooks at the current nesting depth. Thus avoiding the problem of overhead with tracking #Name etc
Btw just wanted to say, I've been super busy with other things, but really, really excited to see the amount of progress with this!
I'm really excited to jump into editor stuff once the infra is ready
Reviewing https://github.com/bevyengine/bevy/pull/23329 now. Should be quite useful for this group's work too
So my current thought is that after 0.19 releases I'll introduce (1) a dynamic-bsn PR, with a BSN viewer tool to form a level 1 editor; (2) a third-party bevy_overrides crate that introduces an @override template that allows for post-spawn overrides based on a CSS-selector-like syntax.
Sure thing!
Yeah I'm thinking roughly these steps:
- Determine child patches / store on context
- Resolve inheritance / spawn inherited scene
2.5. While spawning descendants, check if there is an active patch, and if so, apply the patch
That will have minor performance implications (as we'll need to compute and store the child patches in a map, then copy the relevant templates and apply the patches on top). I think in general this feature is best used in the context of editor-driven scenes, which can generally be fully resolved ahead of time.
But my plan for that is to add a "ResolvedScene::flatten" that operates on the unflattened scene and outputs a final flattened ResolvedScene
So might as well implement it in such a way that supports it in the unflattened context
Kind of. The way inheritance works, a given inherited scene will have a pre-computed ResolvedScene. When computing that ResolvedScene, we don't yet know if a scene in the future will want to patch one of the children
Your proposed strategy would work in cases where we're re-computing the whole scene without a shared/precomputed ResolvedScene (for the inherited scene)
great to have this in-tree, I've seen a few projects (including mine) adopt this approach already π
if we were to also get a solution to https://github.com/bevyengine/bevy/issues/17984 the handles-in-scenes problem would be solved fully
I think it's worth considering as I don't think BSN can provide good workarounds for this, as the asset (e.g. large model file) might not already be loaded when the scene is resolved/spawned
BSN supports "asset dependencies" via Scene::register_dependencies. Currently this is used to ensure the inherited ScenePatch asset is present when calling Scene::resolve, but it was built with the intention to support "required assets" that must be present before spawning
Ex: I have plans to expand HandleTemplate to be configurable to require the asset to be loaded before spawning. This can be done viaTemplate::register_data (which hasn't been fully wired up yet and should actually probably be removed in the short term)
Yesterday was a bunch of doc writing. Today I should hopefully be able to wrap that up. Then clippy stuff, then PR
After the PR is out I'll investigate what to do about impl Default for Handle to make BSN less invasive in the short term.
(which would also cut down on the size of the PR a bit)
There are currently a number of gaps / inconsistencies in the "scene functionality" extension traits for World / EntityWorldMut / Commands / EntityCommands that I've encountered as I've documented them. I'm currently filling in those gaps / refactoring some things
@split harness I just realized, if we make feathers bsn-only, that screws bevy_immediate, which also uses feathers
I suppose I should just bite the bullet and do both BSN and non-BSN versions of everything for consistency
I think its actually not a huge problem. The internals all operate directly on the components. And the feathers example uses on_spawn_insert(feathers_widget_bundle). I'm reasonably certain they could add an on_spawn_apply_scene variant.
My work here involves adding entity_mut.apply_scene(bsn) and entity_commands.apply_scene(bsn), which they could call from on_spawn_apply_scene
In fact, they already have on_spawn_apply_commands, which supports this without any changes to bevy_immediate needed:
ui.ch().on_spawn_apply_commands(|commands| {
commands.apply_scene(bsn! { button(ButtonProps::default()) });
})
I'm still in camp "feathers should probably just use BSN". The current bundle approach is pretty nasty / hackey, can't support every widget type (at least not cleanly), and would require a bunch of redundant work / keeping things in sync.
Although I'm not opposed enough to "supporting bundles where we can" to advocate strongly for their removal.
I agree with this too
@cloud fog Talking about making feathers "bsn-only" - what impact will that have on you, what can we do to make the migration easier?
Would it help to have a brief migration period of both bsn and non-bsn versions of feathers? By "brief" I'm talking maybe a few weeks.
wouldn't that have to be a full release cycle at this point if it is landing for 0.19? or are you thinking about not landing feathers for the release?
I'm sure immediate could work with bsn?
It depends on the release schedule of the crate. Most developers wait until release to do migrations, but some do it early using main
That is the exact conclusion I came to above
It's always harder to do a migration if you have to make all the changes before the code will even compile
I see no real harm in deprecating the existing bsn bundles / leaving them in for a release
/ I think thats what we should do
Both experimental AND deprecated! Achievment unlocked!
Got that C++ level features now we just need to then undeprecate it 4 versions later/j
Just pushed the following changes:
- Filled in the remaining missing docs.
- Added ResolvedSceneRoot and ResolvedSceneListRoot (replacing the
(ResolvedScene, EntityScopes)and(Vec<ResolvedScene>, EntityScopes)tuples). This cleaned up a lot of code. - Removed TemplateField / template field syntax, as it isn't currently used and I don't currently think it is the right path.
- Added "queued entity" cleanup for removed scenes
- Resolved clippy's concerns
Just need to write up a PR description now. Once the PR is out I'm going to flesh out Scene docs a bit more and investigate the Handle::default() situation
Bevy is finally catching up to Unity.
-# oops, sorry that was meant to be a pingless reply
Still need an editor that crashes only slightly less than Bethesda's creation kit.
BSN PR OPEN!!!
i waited 2 minutes before posting
Bawhaha you are quick on the draw
Hehe as if I wasn't going to post here
you mean you lowered the F5 frequency to 2 minutes over the past months
lol
it will be quite an act to review this before the 0.19 π¨
A lot of the lines of code are benchmarks and Handle::default workarounds.
(and docs)
Should hopefully be removing Handle::default stuff shortly
fyi this is lacking syntax highlighting and has an extra newline
I don't want to pollute the PR thread, but the line Ok(entity.resource::<AssetServer>().load(&self.path)) in the PR description should be Ok(context.resource::<AssetServer>().load(&self.path))
@split harness So first off, congratulations π Secondly, there are some issues which were raised in previous discussions / prs which were never really answered, unfortunately these are old enough that I'm having trouble finding links to them. The way in which you use the term Scene is still nails-on-a-chalkboard to me, but I suspect that this is a lost cause.
Fixed
Fixed
You are welcome to bring up whatever questions or issues you want.
I am unlikely to change my stance on Scene. I think it is the correct name, for the reasons we have already discussed.
Unfortunately I've lost the discussion, so I don't know where it was left off
How inaccurate would it be to rename the current bevy_scene to something like bevy_snapshot for the "world serialization" scenarios? You can use it to serialize the current state of the world into a kind of "snapshot", with optional filtering
(also we might want to reserve that name either way maybe, it's currently free on crates.io)
From my perspective, a Scene is a description of what resulting entities should look like. Scene could be a .bsn file, the result of a function, a variable storing that description, etc.
These descriptions are inherently recursive / composable, which is why we implement Scene for things like "single template patches".
A "scene", from a user perspective, is whatever they write to describe it
Ex: this is the "level scene" file, this is the "widget scene" function
Let me summarize / say my piece and be done:
- Metaphorically, your
Scenetrait is analogous to a recipe / blueprint / schematic - it is not the final product - However, the way I have always heard the term "scene graph API" used was that a "scene" was the final product
- If it were up to be, I'd call the trait something like
SceneSpecto emphasize it's recipe nature.
Have you already read the bevy_scene -> bevy_ecs_serialization proposal?
yes
I think bevy_snapshot (or bevy_ecs_snapshot) would be reasonable. Although I think bevy_ecs_serialization is more descriptive of the problem space
bevy_ecs_serialization is alright too but a bit long and unnatural / feels out of place with our other primarily two-word crate names
Given that this is likely to be replaced eventually / is currently a pretty niche use case, I'm not sure it needs a snappy / marketable name
I think we could also consider merging it into bevy_ecs under a serialization feature
yeah, if we're confident that it'll most likely be temporary then I don't really care what the name is
so bevy_ecs_serialization is fine in that case
the syntax is glorious
huh wait. i don't understand
:list_widget({bsn_list! [
Node { width: Px(4.) },
Node { width: Px(5.) },
]})
This is inheritance with a function parameter, but:
Note that because inheritance is cached / pre-resolved, function inheritance does not support function parameters. You can still use parameterized scene functions by defining them directly in the scene (rather than using inheritance):
Oops thats vestigial. I should have that be list_widget() for now
Fixed
pog
If we don't need parameters, what is the difference between :button and button()?
Thank you @split harness!!
Currently they are equivalent, but soon :button will be pre-computed / cached
Just like :βscene.bsnβ currently is
fn player() -> impl Scene {
bsn! {
#Body
Children [
#Head
]
}
}
fn blue_player() -> impl Scene {
bsn! {
:player
// Can we add components to the #Head here?
}
}
@split harness Did you end up removing scene://? I can't seem to find it
Not yet. This is the planned "Descendant Patching" feature in the "What's Next" section
I removed the load_scene helper, but this is still technically possible because I made load_with_path public. I use it in the benchmarks
I would prefer we don't expose load_with_path, especially since we can cheat by just creating a fake loader that returns the right ScenePatch asset
It makes the test a little more complicated, but I don't think that's a big deal.
Let me PR your branch
Oh but I guess that allows users to add their own ScenePatchs that are accessible anywhere, but only defined at runtime.
What if we made a runtime:// asset source? It would be a source ID hard-coded to avoid all the loading business and only give you access to assets that are explicitly registered to it. That seems useful in general, though having a weird super secret asset source name might be a little too cursed
It is best to think of GetTemplate as an alternative to Default for types that require world/spawn context to instantiate
Isin't this describing FromWorld?
Or alternatively, could we support like:
let parent_scene = assets.add(todo!());
bsn! {
:{parent_scene}
}
@split harness I also want to push back on this statement here:
It is generally considered best practice to wrap related entities with more than one entry in () to improve legibility.
I've written a fair amount of code like in this style, and I don't much like it. Given the options that I have available atm, I tend to standardize on no extra parens, but putting the comma on a line by itself:
[
Foo
Bar
Baz
,
Foo
Bar
Baz
]
Maybe not the prettiest, but easy to read and maintain
More generally, I am on team "separators, not brackets" for delineating between sibling entities.
@split harness Also, I don't know if we ever resolved the issue I ran into with dynamic conditional children: I need a way to delete the existing children and insert a new child scene list with no gaps or flickering. This means we can't "delete the children now, but insert the new scene list when it resolves" as seperate operations - both have to happen in the same tick, which means holding back on the delete until resolve completes.
this is immediately what came to mind for me too
from my reading, its a "stateful/serializable/dynamic" FromWorld, in that you can clone and mutate them and save/load them from disc
you can't exactly do that with FromWorld impl because it takes no self
Yeah probably better than exposing something we'd prefer not to
The "spawn context" is the differentiator. FromWorld only has access to World. It doesn't have access to things like the current entity (expressed as EntityWorldMut), or "entity scopes" (for resolving entity references in templates to actual entities elsewhere in the scene)
^ @golden notch
^ @white lichen
yeah that makes sense
templates have a notion of self right?
or only entity?
like i can have a struct with some data in it impl GetTemplate and then that data can be Entity or something else
Yeah this is negotiable. I personally think the comma separator without parens for multi-template entities is pretty odd. But saving the identation / newlines is certainly nice. The final child syntax is also still on the table . But I've intentionally punted that in favor of landing something.
I don't fully understand this question π
Templates have access to &self
A struct that impls GetTemplate can have an Entity field, which the Template might represent as an EntityReference template
On a sidenote, given that resources are components now, FromWorld could have EntityWorldMut now instead of &mut World
@rare whale
I think I will be able to integrate bsn-only bevy_feathers widgets with bevy_immediate. So you can move forward with bsn-only approach π
Out of scope for landing the initial BSN pr for 0.19 but are eventual syntax improvements still on the table? Particularly keen on using # for entities instead of names/flecs-esque names being given special treatment
https://github.com/bevyengine/bevy/pull/20158#discussion_r2211286817
FromWorld isn't just used to instantiate a Resource. It would also require reworking our resource init code
I think they are suitably different to justify separate traits.
Yup! #1264881140007702558 message
@split harness One thought for dealing with the "atomic children replacement" problem - which would also make composing relation more flexible - would be a set of "relation combinators":
Children [
@Prepend([
child,
child,
])
]
Where the combinators are Prepend, Append, Insert and Replace, with Append being the default if there's no combinator. Note that for my reactive use case, these combinators would need to be supported at the top level within bsn_list! when calling spawn_related.
Re-reading the PR, I see that spawn_scene is now immediate - so I guess for my case I can just call despawn_related beforehand and there should be no gap, right?