mirror of
https://github.com/bevyengine/bevy.git
synced 2026-07-01 08:12:51 -04:00
create-pull-request/patch
14 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
535cf401cc |
Reframe old "scene" terminology as "world serialization" (#23630)
Part 2 of #23619 In **Bevy 0.19** we are landing a subset of Bevy's Next Generation Scene system (often known as BSN), which now lives in the `bevy_scene` / `bevy::scene` crate. However the old `bevy_scene` system still needs to stick around for a bit longer, as it provides some features that Bevy's Next Generation Scene system doesn't (yet!): 1. It is not _yet_ possible to write a World _to_ BSN, so the old system is still necessary for "round trip World serialization". 2. The GLTF scene loader has not yet been ported to BSN, so the old system is still necessary to spawn GLTF scenes in Bevy. For this reason, we have renamed the old `bevy_scene` crate to `bevy_world_serialization`. If you were referencing `bevy_scene::*` or `bevy::scene::*` types, rename those paths to `bevy_world_serialization::*` and `bevy::world_serialization::*` respectively. Additionally, to avoid confusion / conflicts with the new scene system, all "scene" terminology / types have been reframed as "world serialization": - `Scene` -> `WorldAsset` (as this was always just a World wrapper) - `SceneRoot` -> `WorldAssetRoot` - `DynamicScene` -> `DynamicWorld` - `DynamicScene::from_scene` -> `DynamicWorld::from_world_asset` - `DynamicSceneBuilder` -> `DynamicWorldBuilder` - `DynamicSceneRoot` -> `DynamicWorldRoot` - `SceneInstanceReady` -> `WorldInstanceReady` - `SceneLoader` -> `WorldAssetLoader` - `ScenePlugin` -> `WorldSerializationPlugin` - `SceneRootTemplate` -> `WorldAssetRootTemplate` - `SceneSpawner` -> `WorldInstanceSpawner` - `SceneFilter` -> `WorldFilter` - `SceneLoaderError` -> `WorldAssetLoaderError` - `SceneSpawnError` -> `WorldInstanceSpawnError` Note that I went with `bevy_world_serialization` over `bevy_ecs_serialization`, as that is what all of the internal features described themselves as. I think it is both more specific and does a better job of making itself decoupled from `bevy_ecs` proper. |
||
|
|
bce6592e77 |
Async handler hook mesh (#23376)
# Objective the Gltf on_gltf_primitive hook is reasonably hard to use. We don't have a "straight to gpu" `bevy_mesh::Mesh` format, which means our glTF support is predicated on doing transformations like draco, meshopt, etc on load. This makes Bevy's mesh interface, well, `bevy_mesh::Mesh`. Continuing that thread, extensions like [draco mesh compression](https://github.com/KhronosGroup/glTF/blob/f966a3fbabeaed32f3f807750e35aaffc59bf4ca/extensions/2.0/Khronos/KHR_draco_mesh_compression/README.md) are **fully responsible** for decompression in the loading process. While this somewhat obviously includes vertex data, this includes *all attributes* including position, normal, weights, joints, and custom data. This makes it the responsibility *of the draco decompression processing* to turn the compressed data into a `Mesh`, and our APIs should reflect that rather than providing gltf objects to users. ## Solution To do this, we must allow the extension to create its own `Mesh`, as well as pass any settings from the loader that are relevant, like `load_meshes`, into the hook. This means the responsibility for creating the `Mesh`, handling decompression, calling the appropriate conversion functions, etc is more directly placed on the extension processor. ## Testing ran a bunch of the gltf examples, including animated mesh and extension examples, and they run as usual. The "regular path" should be unchanged. Only the path through the primitive hook should have any differences. --- ## Showcase [this codebase](https://github.com/ChristopherBiscardi/async-primitive-hook-gltf-extension-bevy/blob/d7f1622b170b4f6450ce5c3a51f8aa7f2780fc84/src/lib.rs) is incredibly messy, but proves the point well. There is no mention of gltf Documents in user code. By contrast, @jiangheng90 has wonderfully provided an *actual* draco implementation [here](https://github.com/jiangheng90-opensource/bevy_gltf_draco/blob/a8d19783e9cbc24acadda66db2a773d5fc447fd6/src/khr_draco_mesh_compression.rs) which uses the current gltf-document-related apis. By doing this we simplify handling for users, giving more control to compression implementations, and reduce the "weird error cases" the previous API had. This image has a regular gltf on the left, and the draco compressed version on the right. <img width="1250" height="782" alt="screenshot-2026-03-15-at-20 36 39@2x" src="https://github.com/user-attachments/assets/6c7cec6f-1f95-4126-8223-3a82aecac5ae" /> Builds on top of #23026 to make sure we keep the async support. --------- Co-authored-by: jiangheng <jiangheng90@live.com> |
||
|
|
9762c65926 |
Enable modification of AnimationClips in glTF processing (#23305)
# Objective Enable mutable access to `AnimationClip`s loaded in the glTF loader, so that events can be added to them. This is an alternative to https://github.com/bevyengine/bevy/pull/23199 that doesn't use handles to access `AnimationClip`s. ## Solution Replace the old individual `on_animation` hook that passed a `Handle<AnimationClip>` with a version that passes exclusive access to the `AnimationClip`. This requires moving the hook in the loader code to pass access through before the handles are created. ```rust /// Called when an individual animation is processed fn on_animation( &mut self, load_context: &mut LoadContext<'_>, gltf_animation: &gltf::Animation, animation_clip: &mut AnimationClip, ) { } ``` This has the drawback of needing to separate the adding of `AnimationClip` events and the actual usage of the `Handle<AnimationClip>` as they are not accessible at the same time. ## Testing ``` cargo run --example gltf_extension_animation_graph ``` ## Showcase Added the smoke particles from the non-gltf animation events example. https://github.com/user-attachments/assets/1570fb4e-7fbd-4c8d-97bf-5fc21cc6592f |
||
|
|
380e894e82 |
rename gltf_render_enabled to gltf_enable_standard_materials (#22944)
# Objective We currently describe the new `gltf_render_enabled` flag as "disabling PBR rendering", which is much broader than what it does in practice. In its current form it controls the construction and insertion of `StandardMaterial`s on entities, but does not affect the PBR rendering functionality of the application, or of glTF assets. (A user could still create their own `StandardMaterial`s or `StandardMaterial` extensions and insert them on the required meshes). ## Solution Rename the flag to more accurately depict what it does in practice, which is enable/disable the automatic construction and insertion of `StandardMaterial`s when a glTF file is loading. ## Testing There's only one example that turns this off, `gltf_extension_mesh_2d`, which continues to run as expected. Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
9fd2637846 |
Add tools to avoid unnecessary AssetEvent::Modified events that lead to rendering performance costs (#16751) (#22460)
# Objective - Fixes #16751 ## Solution - `Assets::get_mut` now returns a wrapper `AssetMut` type instead of `&mut impl Asset`. - `AssetMut` implements `Deref` and `DerefMut`. - `DerefMut` marks assets as changed. - when dropped `AssetMut` will add `AssetEvent::Modified` event to a queue only in case asset was marked as changed. ## Testing - Did you test these changes? If so, how? - No unit tests were added, change is pretty straightforward. - Test project: https://github.com/MatrixDev/bevy-feature-16751-test. - With change: ~100 fps. - Without change: ~15 fps. - Are there any parts that need more testing? - I don't really see how this can break anything or add a measurable overhead. - `AssetEvent::Modified` will now be sent after the asset was modified instead of before. It should not affect anything but still worth noting. - How can other people (reviewers) test your changes? Is there anything specific they need to know? - Have a big amount of entities that constantly update their materials. - Properties of those materials should be animated in a stepped maned (like changing color every 0.1 seconds). - Update material only if value has actually changed: ```rust if material.base_color != new_color { material.base_color = new_color; } ``` - If relevant, what platforms did you test these changes on, and are there any important ones you can't test? - tested on macos (Mackbook M1) - not a platform-specific issue PS: This is my first PR, so please don’t judge. |
||
|
|
0af11aa692 |
Invert bevy_gltf dependency with bevy_pbr (#22569)
# Objective - `bevy_gltf` depends on `bevy_pbr` , with a tight coupling between the scene definition and the rendering of the scene. This prevents artistic rendering of GLTFs. ## Solution - `bevy_gltf` loads as a `GltfMaterial` , and `PbrPlugin` inserts an extension to translator to `StandardMaterial` - Based on feedback from https://github.com/Zeophlite/bevy/pull/6 ## Testing - `cargo run --example animated_mesh` --------- Co-authored-by: atlv <email@atlasdostal.com> |
||
|
|
6ca4769128 |
Minimal responsive FontSize support (#22614)
# Objective
Add responsive font sizes supporting rem and viewport units to
`bevy_text` with minimal changes to the APIs and systems.
## Solution
Introduce a new `FontSize` enum:
```rust
pub enum FontSize {
/// Font Size in logical pixels.
Px(f32),
/// Font size as a percentage of the viewport width.
Vw(f32),
/// Font size as a percentage of the viewport height.
Vh(f32),
/// Font size as a percentage of the smaller of the viewport width and height.
VMin(f32),
/// Font size as a percentage of the larger of the viewport width and height.
VMax(f32),
/// Font Size relative to the value of the `RemSize` resource.
Rem(f32),
}
```
This replaces the `f32` value of `TextFont`'s `font_size` field.
The viewport variants work the same way as their respective `Val`
counterparts.
`Rem` values are multiplied by the value of the `RemSize` resource
(which newtypes an `f32`).
`FontSize` provides an `eval` method that takes a logical viewport size
and rem base size and returns an `f32` logical font size. The resolved
logical font size is then written into the `Attributes` passed to Cosmic
Text by `TextPipeline::update_buffer`.
Any text implementation using `bevy_text` must now provide viewport and
rem base values when calling `TextPipeline::update_buffer` or
`create_measure`.
`Text2d` uses the size of the primary window to resolve viewport values
(or `Vec2::splat(1000)` if no primary window is found). This is a
deliberate compromise, a single `Text2d` can be rendered to multiple
viewports using `RenderLayers`, so it's difficult to find a rule for
which viewport size should be chosen.
### Change detection
`ComputedTextBlock` has two new fields: `uses_viewport_sizes` and
`uses_rem_sizes`, which are set to true in `TextPipeline::update_buffer`
iff any text section in the block uses viewport or rem font sizes,
respectively.
The `ComputedTextBlock::needs_rerender` method has been modified to take
take two bool parameters:
```rust
pub fn needs_rerender(
&self,
is_viewport_size_changed: bool,
is_rem_size_changed: bool,
) -> bool {
self.needs_rerender
|| (is_viewport_size_changed && self.uses_viewport_sizes)
|| (is_rem_size_changed && self.uses_rem_sizes)
}
```
This ensures that text reupdates will also be scheduled if one of the text section's uses a viewport font size and the local viewport size changed, or if one of the text section's uses a rem font size and the rem size changed.
#### Limitations
There are some limitations because we don't have any sort of font style inheritance yet:
* "rem" units aren't proper rem units, and just based on the value of a resource.
* "em" units are resolved based on inherited font size, so can't be implemented without inheritance support.
#### Notes
* This PR is quite small and not very technical. Reviewers don't need to be especially familiar with `bevy_text`. Most of the changes are to the examples.
* We could consider using `Val` instead of `FontSize`, then we could use `Val`'s constructor functions which would be much nicer, but some variants might not have sensible interpretations in both UI and Text2d contexts. Also we'd have to make `Val` accessible to `bevy_text`.
## Testing
The changes to the text systems are relatively trivial and easy to understand. I already added a minor change to the `text` example to use `Vh` font size for the "hello bevy" text in the bottom right corner. If you change the size of the window, you should see the text change size in response. The text initially flickers before it updates because of some unrelated asset/image changes that mean that font textures aren't ready until the frame after the text update that changes the font size.
Most of the example migrations were automated using regular expressions, and there are bound to be mistakes in those changes. It's infeasible to check every single example thoroughly, but it's early enough in the release cycle that I don't think we should be too worried if a few bugs slip in.
---------
Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com>
|
||
|
|
487d9729c8 |
re-enable gltf extension handler examples on wasm (#22591)
# Objective Currently the `GltfExtensionHandler` examples are disabled on wasm due to an issue with API availability on wasm ## Solution Use the correct APIs in the examples, flagged by platform. ## Testing ``` bevy run --example gltf_extension_animation_graph web bevy run --example gltf_extension_mesh_2d web ``` ## Showcase <img width="1920" height="1080" alt="screenshot-2026-01-18-at-16 04 48" src="https://github.com/user-attachments/assets/d6a42c52-48da-4ad7-b4c2-ea512a06a5e5" /> <img width="1920" height="1080" alt="screenshot-2026-01-18-at-16 05 20" src="https://github.com/user-attachments/assets/b2066d3a-e07e-43f6-9490-0ca0f77e5810" /> |
||
|
|
a88af65738 |
Contact Shadows (#22382)
# Objective - Implement contact shadows to add fine shadow detail where shadow cascades cannot. ## Solution - Extend our existing pbr implementation using our existing raymarching functions. --- ## Showcase <img width="1824" height="1180" alt="image" src="https://github.com/user-attachments/assets/e93b79c5-c596-4a9e-b94d-20bdde1d863b" /> <img width="1824" height="1180" alt="image" src="https://github.com/user-attachments/assets/0fd7dffa-60b8-4b92-8fad-7f993d4d89dd" /> https://github.com/user-attachments/assets/e74b190d-9ae3-4aaf-97f0-b520930a0667 https://github.com/user-attachments/assets/e80ccb26-bbaa-4d25-a823-8ea12354c5b9 https://github.com/user-attachments/assets/b04f4b00-92bd-4a2f-b7dd-5157d8fbe0ab <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/b7629908-dd32-48db-8ee7-a4d2dd8f66c2" /> <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/3de0258e-9191-4180-ac57-41b32e1205bd" /> <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/951477f9-e9a9-426f-ae8d-18ae50cc7b85" /> <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/2291453c-da57-4fcc-a6b0-f60f6eac6cbb" /> <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/5820cdff-ea54-4294-b520-2a8d8dc24996" /> <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/3ea16481-7689-4e99-87e2-1589f1532e4c" /> --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: charlotte 🌸 <charlotte.c.mcelwain@gmail.com> |
||
|
|
86224b5cb2 |
Refactor GltfExtensionHandler Hooks (#22114)
As it turns out, many glTF extensions require accessing the data from other extensions. Trying to isolate and scope access is much less helpful than exposing the glTF objects for consumers to take what they want. This includes - extension data in the "others" category (anything the gltf crate doesn't support explicitly) - the functions that return the extension data the gltf crate *does* have hardcoded support for - names and any other available data --- The diff for users is that they - no longer have to worry about defining extension ids to process - no longer have to worry about multiple calls due to those ids - now have to use `.extension_value()`, `.name()`, or similar to get relevant data - Can now access `.light()` or any other data built-in to the gltf crate, such as [`.variants`](https://docs.rs/gltf/1.4.1/gltf/struct.Document.html#method.variants) for `KHR_materials_variants`. An example diff on the user side can be viewed in the update commit for the Skein PR: https://github.com/rust-adventure/skein/pull/89/changes/ac1e510c9d136d60c23decd68b10f35f6e24f76b |
||
|
|
e7b64b6b04 |
Add support for arbitrary/third party glTF Extension processing via GltfExtensionHandler (#22106)
# Objective Currently Bevy doesn't support arbitrary glTF extensions. The ones it does support are hardcoded. We should support glTF extensions, as this is a primary mechanism for sharing behavior via data exported from applications like Blender. I personally have found usecases in exporting component data, lightmap textures/information, and processing other kinds of data (AnimationGraph, 3d meshes into 2d, etc). ## Solution This PR introduces a new `GltfExtensionHandler` trait that users can implement and add to the glTF loader processing via inserting into a Resource. There are two example processors currently added, with a third that I'd like to add after this PR. - `examples/gltf/gltf_extension_animation_graph.rs` duplicates the functionality of `animation_mesh`, constructing AnimationGraphs via extension processing and applying them to be played on the relevant nodes. - `examples/gltf/gltf_extension_mesh_2d.rs` duplicates the functionality of the `custom_gltf_vertex_attribute` example, showing how the extension processing could be used to convert 3d meshes to 2d meshes alongside custom materials. Both of these examples re-use existing assets and thus don't *actually use* extension data, but show how one could access the relevant data to say, only convert specifically labelled Mesh3ds to 2d, or process many animations into multiple graphs based on extension-data based labelling introduced in Blender. A third example I want to introduce after this PR is the same core functionality Skein requires: an example that uses reflected component data stored in glTF extensions and inserts that data onto the relevant entities, resulting in scenes that are "ready to go". ## Comparison to Extras In comparison to extensions: data placed in glTF extras is well supported through the `GltfExtras` category of components. Extras only support adding an additional `extras` field to any object. Data stored in extras is application-specific. It should be usable by Bevy developers to implement their own, application-specific, data transfer. This is supported by applications like Blender through the application of Custom Properties. Once data is used by more than one application, it belongs in a glTF extension. ## What is a glTF Extension? Extensions are named with a prefix like `KHR` or `EXT`. Bevy has already reserved the `BEVY` namespace for this, which is listed in the official [prefix list](https://github.com/KhronosGroup/glTF/blob/7bbd90978cad06389eee3a36882c5ef2f2039faf/extensions/Prefixes.md). For a glTF file, an extension must be listed in `extensionsUsed` and optionally `extensionsRequired`. ``` { "extensionsRequired": [ "KHR_texture_transform" ], "extensionsUsed": [ "KHR_texture_transform" ] } ``` Extension data is allowed in any place extras are also allowed, but also allow much more flexibility. Extensions are also allowed to define global data, add additional binary chunks, and more. For meshes, extensions can add additional attribute names, accessor types, and/or component types `KHR_lights_punctual` is a contained and understandable example of an extension: https://github.com/KhronosGroup/glTF/blob/7bbd90978cad06389eee3a36882c5ef2f2039faf/extensions/2.0/Khronos/KHR_lights_punctual/README.md . This one happens to be already hardcoded into Bevy's handling, so it doesn't benefit from arbitrary extension processing, but there are additional [ratified](https://github.com/KhronosGroup/glTF/tree/7bbd90978cad06389eee3a36882c5ef2f2039faf/extensions#ratified-khronos-extensions) and [in-progress](https://github.com/KhronosGroup/glTF/tree/7bbd90978cad06389eee3a36882c5ef2f2039faf/extensions#in-progress-khronos-and-multi-vendor-extensions-and-projects) extensions, as well as [vendor](https://github.com/KhronosGroup/glTF/tree/7bbd90978cad06389eee3a36882c5ef2f2039faf/extensions#vendor-extensions) and other arbitrary extensions that would benefit from userland support. ## Implementation This initial implementation is reasonably minimal: enabling extension processing for objects/etc as they're loaded which may also define extension data, including the scene world. This may leave out useful functionality; as detailed in the next section: "What's not implemented". Extension handlers are defined by implementing a trait which can optionally define hooks and data. Extension handler data is cloned to start with a fresh slate for each glTF load, which limits scope to "one glTF load". So while state can be maintained across hooks during a single load, users who want to combine or handle multiple glTF assets should do so in the main app, not in an extension handler. Following this, because the extensions are stored as `dyn GltfExtension` *and* we want to clone them to isolate state to a single load, `dyn_clone` must be included as a workaround to enable this cloning. An extension handler has to be added to the list of handler by accessing a `Resource` and pushing an instantiated handler into it. This Resource keeps the list of extension handlers so that a new glTF loader can bootstrap them. The design of the hooks is such that: - If no extensions handlers are registered, none are called for processing - If an extension handler is defined, it receives all "events" - handlers are defined by a trait, and default implementations are called if an override is not specified. - default implementations are no-ops It is important that extensions receive all events because certain information is not embedded in extension data. For example, processing animation data into an animation graph could require both processing animations with extension data, tracking the animation roots through hooks like `on_node`, *and* applying those graphs in the `on_scene_completed` hook. - Extension data is passed to hooks as `Option<&serde_json::Value>` which is only passing references around as the data has already been converted to `Value` by the `gltf` crate. - `LoadContext` is required for creating any new additional assets, like `AnimationGraph`s. - *scene* World access is provided in hooks like `on_scene_completed`, which allows calculating data over the course of a glTF load and applying it to a Scene. ### What's not implemented This PR chooses to *not* implement some features that it could. Instead the approach in this PR is to offer up the data that Bevy has already processed to extensions to do more with that data. - Overriding `load_image`/`process_loaded_texture` - This could allow projects like bevy_web_codecs, [which currently forks the entire gltf loader](https://github.com/jf908/bevy_web_codecs/tree/373bbf29be6555c7603fd6867a01159ab0f20fed/bevy_web_codecs_gltf). Associated [issue](https://github.com/bevyengine/bevy/issues/21185). However I believe this needs some design work dedicated to what exactly happens here to support that use case. - This PR doesn't include any refactoring of the glTF loader, which I feel is important for a first merge. - ~~There is some benefit to passing in the relevant `gltf::*` object to every hook. For example, I believe this is the only way to access extension data for `KHR_lights_punctual`, and [`KHR_materials_variants`](https://docs.rs/gltf/1.4.1/gltf/struct.Document.html#method.variants) or other extensions with "built-in" support. I haven't done this in all places.~~ (edit: after external implementation I decided this was a good idea and added it to more places) ## Testing ``` cargo run --example gltf_extension_animation_graph cargo run --example gltf_extension_mesh_2d ``` --- ## Showcase Both examples running: https://github.com/user-attachments/assets/f9e7c3c9-cdad-4d33-ace7-7c2ca5469d5e https://github.com/user-attachments/assets/baa9bc92-ca3b-46ad-a3f0-2f74bbc29b68 <details> <summary>An example that showcases converting Mesh3d to Mesh2d</summary> ```rust #[derive(Default, Clone)] struct GltfExtensionProcessorToMesh2d; impl GltfExtensionProcessor for GltfExtensionProcessorToMesh2d { fn extension_ids(&self) -> &'static [&'static str] { &[""] } fn dyn_clone(&self) -> Box<dyn GltfExtensionHandler> { Box::new((*self).clone()) } fn on_spawn_mesh_and_material( &mut self, load_context: &mut LoadContext<'_>, _gltf_node: &gltf::Node, entity: &mut EntityWorldMut, ) { if let Some(mesh3d) = entity.get::<Mesh3d>() && let Some(_) = entity.get::<MeshMaterial3d<StandardMaterial>>() { let material_handle = load_context.add_loaded_labeled_asset("AColorMaterial", (CustomMaterial {}).into()); let mesh_handle = mesh3d.0.clone(); entity .remove::<(Mesh3d, MeshMaterial3d<StandardMaterial>)>() .insert((Mesh2d(mesh_handle), MeshMaterial2d(material_handle.clone()))); } } } ``` </details> --------- Co-authored-by: Kristoffer Søholm <k.soeholm@gmail.com> |
||
|
|
18a7c3092e |
Shortcircuit SceneInstanceReady on models that do not have ColorOverride (#21751)
# Objective The `edit_material_on_gltf` example was iterating over all materials of a Gltf even when it did not have `ColorOverride`. ## Solution Shortcircuit observer if scene does not have `ColorOverride` ## Testing Ran the example |
||
|
|
d431f7e139 |
Split AmbientLight into two (#21595)
# Objective For resources-as-components (#19731), structs mustn't doubly derive both `Component` and `Resource`. ## Solution Split `AmbientLight` in two: `AmbientLight` (the resource) and `AmbientLightOverride` (the component). ## Testing I initially made two structs `AmbientLightComponent` and `AmbientLightResource`, and replaced every mention with the relevant one to ensure that I didn't confuse the two. ## Notes - I don't know if the names are correct. I kept the easiest name for the resource, as that's the one most often used. - I haven't provided any conversion methods as there are already plans to replace the component variant with something else. --------- Co-authored-by: atlv <email@atlasdostal.com> |
||
|
|
6da51ef236 |
move gltf examples to gltf category (#21141)
# Objective In the context of this PR, there are two access patterns users have when reaching for examples: - "I have a glTF file and I need to figure out what to do with it" - "I want to learn about Bevy feature X" Users wanting to learn about Bevy features can go to any appropriately named examples directory to learn more about that feature. Users who have a glTF file currently have to scan different directories to find glTF related functionality. For example: looking in the 2d directory for custom vertex attribute examples. ## Solution Create a glTF category and move applicable examples there. It is notable that a "gltf" directory *could* accept any example that uses a glTF file, but this should not be the case. The examples in this directory should be focused on the "I have a glTF file and I'm trying to figure out how to work with it" access pattern. If a glTF file is used "as data" to support a feature showcase, then it is not "a gltf example". For example: spawning a Scene from a glTF file is not enough to qualify an example for this directory. I chose to call this directory "gltf" instead of using the "scene" directory so as to leave "scene" for bsn examples in the future. ## Testing ``` cargo run --example custom_gltf_vertex_attribute cargo run --example load_gltf cargo run --example load_gltf_extras cargo run --example query_gltf_primitives cargo run --example update_gltf_scene cargo run --example edit_material_on_gltf cargo run --example gltf_skinned_mesh ``` |