-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improved Scene Format #92
Comments
If you need a parser for the scene format, I could probably do that in my free time. I have some fun with parsers. 😄. If you don't mind it for some reason my favorite parser crate is |
If you don't wanna make a new format from scratch, or just wanna make something a little less domain specific, consider using Rusty Object Notation (RON). Syntax highlighting is one thing that springs to mind that would be easier with an existing format. |
Bevy already uses RON, but there is an issue with de-serialising types not given by the caller, I think that's why components are currently all hashmaps/objects. |
|
This is an example of what we were loading in the prototype @kabergstrom and I were working on. We had this loading/editing/saving in a prototype editor. I'm not necessarily proposing it for adoption - just as an existence proof that you can dynamically load component types with RON.
Also another thing we did was separate the concept of serializable and non-serializable components. i.e. if you wanted a RigidBodyComponent, you would need a serializable SphereRigidBodyComponetDef (that might define the radius of the sphere, friction coefficient, etc.) and then a RigidBodyComponent that existed only at runtime that could have a handle to the rigid body in the physics system. (Which obviously.. that handle would not be possible to persist.) This allowed us to have editor-friendly types (like Euler-angle rotations) get transformed into runtime-friendly types (like a simple 4x4 matrix). This approach of separating design-time representation from runtime representation was a key outcome of this R&D effort and made implementing components that rely on complex systems like physics or FFI straightforward: https://community.amethyst.rs/t/atelier-legion-integration-demo/1352 |
I'm open to using RON syntax, but I would prefer it if we designed our ideal format first and then decide the best way to implement it.
Yeah I want to focus on a human-friendly format first and building tooling around that. Then we can decide if there are pain points that require a second format. I'd prefer one format just for simplicity though. I'd be curious to see a case where a human readable format like the ones above isn't a good fit for tooling.
I agree that we should table it for now. Too much complexity for marginal wins. And I agree that it would make writing tools around the format harder.
I've used something similar with C# databases (Entity Framework "migrations"). I agree that a migration tool is nice to have, but in the short term I think I would be willing to settle for returning nice errors when a required field is missing, when a type doesn't match, or when a field is set but it doesn't exist.
Yeah I have a very strong preference to use type names in scenes (and short names where possible). UUIDs do solve the uniqueness problem, but i dont think they're worth the price of legibility and hand-composabiltiy. We should error out when a type name is used in a scene but isn't registered. And we can solve "potential type ambiguity" by either adding an "import" system to scenes or by forcing developers to resolve ambiguity when they register types in their apps.
Agreed. I really like how godot handles scenes and plan on using many of their ideas:
Makes sense. Having multiple scenes is definitely a goal for me (see godot comment above)
Agreed (see my comment about godot scenes above) |
Yes! I really didn't like Godot's node/object oriented design ( which replace with ECS 🎉 ), but their scene and prefab design was awesome, where every scene can include instances of other scenes and be split out to other files. That was great. |
Cases where designing for direct human-editing makes tooling more difficult:
Slightly switching gears from "GUI editor tools" to "tooling support with scene files" but probably also worth mentioning:
I'm not saying these things are blockers. They're just trade-offs that we should keep in consideration. Many are worth it or can be mitigated.
Maybe supporting UUIDs and names would work for types/fields too (i.e. accept both UUID or names). Names could be baked out in "fully cooked" builds so that the engine is only dealing with UUIDs. This also avoids leaking names in a released build. Sorry, this was probably a longer reply than necessary - I think we agree on everything except possibly UUID vs. names, maybe we can come up with a good solution for that (does allowing both in dev and cooking names out in released builds seem reasonable?). |
Cool the examples you provided all seem valid, but I agree that they aren't blockers and are largely "worth it", especially in the short term. Things like "faster file loading" will be largely in the noise. And if we ever hit a point where someone cares because they're loading Skyrim-sized scene files, we can build a new efficient format. "UUID or names" seems like a pragmatic choice. It increase implementation complexity slightly, but it lets people choose what makes sense for their project. I personally think we should default to "names" for scenes generated in the editor because I want "human friendliness" by default. But thats probably out of scope for the current conversation. I think "baking out' names might also need to be a configuration option. Some people might want their released scenes to be human readable. |
(but yeah in general it sounds like we're aligned) |
I would like to add in here that for projects like Arsenal, you will probably for the large majority of scene files create never be editing them by hand. In this case, with large and/or complex scenes I think it would make sense to have a more compact binary format that could be rendered to, similar to how Armory3D did it. You could choose to export scenes as JSON for debug purposes or use a messagepack-based binary representation. I think it would be good if Bevy could optionally load a machine-only binary representation for compactness and speed. If we are using Serde to accomplish the serialization ( which I'm assuming we would be? ) we could probably just use a format like CBOR. We don't want to limit the ability to edit scenes by hand in any way, but I don't think that this effects that negatively. |
Expanding on this, it is probably undesirable to require users to assign unique IDs by hand to each entity or other thing that can be referenced in a scene file, while having a unique ID is a requirement to be able to implement things like field overrides when spawning a nested scene. So in this case, mixing hand-crafted and tool-generated scene files will possibly compromise features that require unique IDs for things in them. |
I'm actually going to take back what I said above about Arsenal probably wanting a binary format for scenes. If nobody else needs it don't worry about it for the Arsenal use-case because Arsenal will almost surely have its own scene format and prefab system to be as compatible and seamless with Blender as possible. |
Just throwing my thoughts into this conversation. |
That's LGPL3 license. So I don't think bevy devs could look at the source code of nyan. |
after a quick reading of nyan's specifications, it should be relatively easy to port |
One thing I'd quite like a potential scene format to support is the ability to reference other scene files. For example, one may have a scene describing an asset composed of multiple entities (eg a table), and then multiple other scene files that contain references to that asset (different rooms). This should be recursive and support fan in (eg building scene -> multiple room scenes -> single table scene) Potentially this should work for both entity-scene-trees (as in the table example above) and for single components. Consider a material component: a combination of image (paths), a shader etc. It is likely that multiple assets will share said material. I guess what I'm saying is that treating scenes as self-contained may have rather big limitations - allowing them to reference other scenes would be very useful. |
Seeing that XML-like demo gist reminds me of WPF XAML, and I think it could be used as an inspiration. Notably, three features of its syntax can be relevant:
In fact, in XAML there is a namespace for the XAML directives themselves. In Bevy's case, it would be directives for the asset loader, which could include a way of referencing entities within the file. Below's a scratch imagining what this could look like, based on cart's example: <Scene>
<Entity>
<MyComponent />
<Scale x="2.0" y="2.0" z="2.0" />
<!-- I recognize this gets rather unwieldly if we stick to XML syntax. -->
<PropDemo value="hello">
<PropDemo.sequence>
<u32>0</u32>
<u32>1</u32>
<u32>2</u32>
</PropDemo.sequence>
<PropDemo.map>
<Pair key="field" value="1.0" />
<Pair key="hello" value="hi" />
</PropDemo.map>
<PropDemo.nested>
<NestedPropsType>
<NestedPropsType.field>
<InnerType test="1.0" />
</NestedPropsType.field>
</NestedPropsType>
</PropDemo.nested>
</PropDemo>
<!-- Just like @cart's idea, loose entities are nested. -->
<Entity id="1223801" />
<!--
Here's an example of a directive, including a scene.
Parametrism could still be achievable like so:
-->
<bevy:Asset path="./ModalScene.xml" param:height="30%">
<!--
And note, since properties can be expanded to tags
and vice versa, this allows arbitrarily complex
parametrism:
-->
<param:Title>
<Entity>
<Text text="Some cool title" />
</Entity>
<param:Title>
<Entity>
<!-- Content of the modal etc... -->
</Entity>
</bevy:Asset>
<!-- A directive could be used for in-scene cross-referencing: -->
<Entity bevy:name="player">
<MovementController speed="5" />
</Entity>
<Entity>
<PerspectiveCamera fov="90" />
<Orbit>
<Orbit.target>
<bevy:Reference name="player"/>
</Orbit.target>
</Orbit>
</Entity>
</Entity>
</Scene> I have no context or idea what was meant with the property/morphism thing. I assume some sort of parameter transformation? But when would it be evaluated, without becoming a de-facto scripting language? I think this is a bit out of scope for scenes themselves so I ignored it in my example. Now, another example that "tries" to be a bit more practical - A hypothetical HUD, which would render a big title with a left-aligned button, kinda like Half-Life 2's main menu. <Scene>
<bevy:Asset path="../fonts/Zilla Slab.otf" bevy:name="TitleFont" />
<!--
Name directives can be used for centralizing values, too!
Combined with parametrism, this could serve as a simple styling language.
-->
<f32 bevy:name="TitleFontSize">32.0</f32>
<!--
Remember, tags are just big attributes!
So this is a valid definition for an entity with the Container component.
-->
<Entity Container="" Name="Background">
<Size width="100%" height="100%" />
<!-- As with @cart's, you can spawn bundles as entities, and override -->
<TextBundle Name="Title">
<Style position_type="Absolute">
<Style.position top="0" left="0" right="0" bottom="50%" />
</Style>
<Text>
<Text.alignment horizontal="Left" vertical="Center" />
<Text.sections>
<TextSection
value="Cool"
style.font="{bevy:Reference name=TitleFont}"
style.font_size="{bevy:Reference name=TitleFontSize}"
style.color="{Color::Rgba green=1.0 blue=1.0 alpha=1.0}"
/>
<TextSection
value="Game"
style.font="{bevy:Reference name=TitleFont}"
style.font_size="{bevy:Reference name=TitleFontSize}"
style.color="{Color::Rgba red=1.0 green=1.0 blue=1.0 alpha=1.0}"
/>
</Text.sections>
</Text>
</Entity>
<ButtonBundle Name="PlayButton">
<!--
If one really doesn't like nesting for mid-complexity props,
the alternative brace syntax is your friend.
-->
<Style
position_type="Absolute"
position="{Rect top=60% left=0 right=0 bottom=0}"
/>
<TextBundle>
<Text alignment="{TextAlignment horizontal=Left vertical=Top}">
<!--
A trait could allow types to take in custom children,
further reducing the need for "frivolous" nesting.
-->
<TextSection
value="Cool"
style.font="{bevy:Reference name=TitleFont}"
style.font_size="32"
style.color="{Color::Rgba green=1.0 blue=1.0 alpha=1.0}"
/>
<TextSection
value="Game"
style.font="{bevy:Reference name=TitleFont}"
style.font_size="32"
style.color="{Color::Rgba red=1.0 green=1.0 blue=1.0 alpha=1.0}"
/>
</Text>
</Entity>
</Entity>
</Entity>
</Scene> With all that said, I don't know if XML is the ideal serialized format, but at least it would provide some sort of existing tooling. Regardless, the concept of directives could be used even without the specific syntax. |
As an alternative to an XML-like, you can also consider KDL, which offers a similarly node-based configuration language but with a bit more modern design. Translating Moxinilian's gist could give us the following valid KDL (modulo my errors):
|
Hello! I'm a rookie in the subject but I was wondering if scenes can reference or have systems attached to them as some sort of high order controllers to manipulate children structures in a more dynamic way? You know, maybe this could simplify a bit the process of adding or removing nested scenes. |
Significantly improved with the assorted changes in 0.9. Closing this out! |
@Moxinilian and I have been experimenting with an improved scene format:
https://gist.github.com/Moxinilian/c45a1858eca7e918b5728ee5c117f649
https://gist.github.com/cart/3e77d6537e1a0979a69de5c6749b6bcb
The current scene format is workable, but it isn't yet ideal for manual scene composition because it is a flat list of unordered entities and it isn't particularly ergonomic. I also want to add nested scenes.
The text was updated successfully, but these errors were encountered: