-
-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Add support for arbitrary/third party glTF Extension processing via GltfExtensionHandler #22106
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
Add support for arbitrary/third party glTF Extension processing via GltfExtensionHandler #22106
Conversation
|
Approved, although I realized that this isn't actually a gltf extensions processor, it's a general gltf post-processor that may or may not have anything to do with extensions. For example, I could probably use this to add outline materials based on gltf extras. |
|
@viridia you could, yes. The generality of extensions basically makes these the same things. All of the processing needs to be in similar places whether you're actually processing the data or not. Even a simpler case of a not-built-in |
…nodes bevy uses to construct it; this in turn makes accessing the data much more straightforward when dealing with the mesh/material entity merge
|
I have also used this PR to implement extension-based handling for Skein, which stores the TypeRegistry in the extension handler and reflected component data in gltf extensions. While loading a gltf, the component data is inserted into the appropriate locations using the underlying infrastructure for insert_reflect. {
"extensions": {
"BEVY_skein": {
"components": [
{
"test_components::Player": {
"name": "Is the Mesh Object",
"power": 90.93000030517578,
"test": -234
}
}
]
}
},
"mesh": 0,
"name": "Cube"
}, |
janhohenheim
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, excellent documentation! Looks all good to me :)
andriyDev
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall LGTM. A couple minor details here but nothing big.
The only thing that is weird is that we're calling it GltfExtensionHandler which as @viridia mentioned is not quite accurate. It would be nice to have a name that indicates it can be used for any sort of manipulation. But that's bikeshedding a little too much.
Yeah I suppose. We could name it I'm not sure the fact that you can ignore the extension data entirely if you want to is worth renaming the type. Naming it I'm open to other names but I think its also fine to rename it in a future release if a better name comes up. |
…atures. (#22125) # Objective - #22106 accidentally added imports for AnimationClip, HashMap, and HashSet - but these are only available/used if the bevy_animation feature is enabled. So running `cargo t -p bevy_gltf` fails to compile and gives warnings! ## Solution - Guard these `use` statements on the bevy_animation feature. ## Testing - Ran `cargo t -p bevy_gltf`. - Ran `cargo t -p bevy_gltf --all-features`.
Release notes for #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
GltfExtensionHandlertrait 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.rsduplicates the functionality ofanimation_mesh, constructing AnimationGraphs via extension processing and applying them to be played on the relevant nodes.examples/gltf/gltf_extension_mesh_2d.rsduplicates the functionality of thecustom_gltf_vertex_attributeexample, 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
GltfExtrascategory of components.Extras only support adding an additional
extrasfield 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
KHRorEXT. Bevy has already reserved theBEVYnamespace for this, which is listed in the official prefix list.For a glTF file, an extension must be listed in
extensionsUsedand optionallyextensionsRequired.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_punctualis 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 and in-progress extensions, as well as vendor 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 GltfExtensionand we want to clone them to isolate state to a single load,dyn_clonemust be included as a workaround to enable this cloning.An extension handler has to be added to the list of handler by accessing a
Resourceand 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:
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 theon_scene_completedhook.Option<&serde_json::Value>which is only passing references around as the data has already been converted toValueby thegltfcrate.LoadContextis required for creating any new additional assets, likeAnimationGraphs.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.
load_image/process_loaded_textureThere is some benefit to passing in the relevant(edit: after external implementation I decided this was a good idea and added it to more places)gltf::*object to every hook. For example, I believe this is the only way to access extension data forKHR_lights_punctual, andKHR_materials_variantsor other extensions with "built-in" support. I haven't done this in all places.Testing
Showcase
Both examples running:
screenshot-2025-12-13-at-05.17.59-converted.mp4
screenshot-2025-12-13-at-05.18.54-converted.mp4
An example that showcases converting Mesh3d to Mesh2d