Skip to content
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

GLTF: Implement KHR_animation_pointer for animating custom properties #94165

Merged
merged 8 commits into from
Nov 5, 2024

Conversation

aaronfranke
Copy link
Member

@aaronfranke aaronfranke commented Jul 10, 2024

EDIT: I suggest merging #96748 first so that we can make this work correctly for models with different UV maps.

This PR implements the glTF Object Model and the KHR_animation_pointer extension. The object model allows accessing properties of the glTF JSON using JSON pointers, mapped to Godot properties. This can be used for things like engine-agnostic scripting systems, where simple behavior graphs can be defined inside of glTF files to manipulate properties.

KHR_animation_pointer is the first extension prominently making use of the object model, to allow for animations to control properties. With vanilla glTF animations, you can animate position, rotation, scale, and weights. However, animation pointer allows animating much more. For example, you could animate the color of a light, the FOV of a camera, the albedo color of a material, the UV offset of a material, and more.

khr_anim_ptr.mp4

Test project: gltf_khr_animation_pointer.zip

These test files were created by the Khronos group, not me. The source is here: KhronosGroup/glTF-Sample-Assets#106

Both the object model and the KHR_animation_pointer extension are already final and ratified by the Khronos group. This must be core to Godot Engine because it requires a lot of low-level hooks to do it right, and other extensions need to be able to build on these features. See also godotengine/godot-proposals#10164

For ease of reviewability, I have split this PR into 8 commits:

  • GLTF: Move the component type enum into GLTFAccessor
    • Also includes exposing and documenting the component types.
  • GLTF: Add more accessor component types
    • Not actually used in any code path when exporting, but allows importing more data types. This will become useful in the future for extensions like KHR_interactivity that can utilize data encoded with these types, or if new extensions use these data types for their buffers.
  • GLTF: Clean up animation code to make way for KHR_animation_pointer
    • This commit will be the most annoying to review. It overhauls the GLTF animation import and export code, so it's hard to get a detailed look at the changes from the diff.
    • Some specifics: Use double for animation times consistently (in the old code it was converting between double and float back and forth several times), rename Track to NodeTrack (to make way for pointer tracks), add static methods to GLTFAnimation to convert between Godot and GLTF interpolation types, move bone and Skeleton3D bone handling code to its own function _node_and_or_bone_to_gltf_node_index, overhaul and simplify _convert_animation.
  • GLTF: Prerequisite cleanups before KHR_animation_pointer
    • Add get_scene_node_path and has_additional_data to GLTFNode, fix code order in GLTFDocumentExtension, remove center of mass ignore warning in physics (it's supported now), rename d to mesh_dict in mesh import code.
  • GLTF: Implement the glTF Object Model to support JSON pointer properties
    • This is the exciting/interesting one, and is the one that exposes APIs for using the object model.
    • Includes support for handling object model pointers present in the core glTF spec and in a few extensions such as lights, texture transform, and emissive strength.
  • GLTF: Implement the glTF Object Model in physics extension
    • Allows for animating things like the mass of a physics body or the radius of a sphere collider.
    • I had to rework a bit of the export code to allow for shape indices to be known earlier in the process.
  • GLTF: Add functions to encode and decode Variants to/from accessors
    • Required to import pointer tracks as Variant arrays for use with Godot's animation system.
  • GLTF: Implement KHR_animation_pointer for animating custom properties
    • Bringing all of it together!

If desired, I can move some of these into a separate PR.

@JekSun97
Copy link
Contributor

Wow, I didn’t even know that GLTF could store such data, I was surprised by the waterfall, how does it work? UVs are recorded and animated?

@adamscott
Copy link
Member

Could you add this link when talking about JSON pointers in your intro? I had to search about the concept.
https://datatracker.ietf.org/doc/html/rfc6901

@aaronfranke
Copy link
Member Author

@JekSun97 The UV offset values can be stored in glTF in a KHR_texture_transform. This is then mapped to Godot's BaseMaterial3D uv1_offset. This PR adds support for importing and exporting animations that animate this property, but it's already possible to store a frozen non-animated version of this value before this PR.

@fire
Copy link
Member

fire commented Sep 17, 2024

https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_animation_pointer is a ratified official extension so as soon as the Godot Engine portion is done, we can merge. Some of the other extensions are still in draft stage.

@aaronfranke aaronfranke requested a review from a team as a code owner October 4, 2024 04:56
Copy link
Contributor

@lyuma lyuma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are currently several cases involving TYPE_VALUE tracks for :scale, :rotation, :quaternion, :position and :transform that I think do not work for cubic interpolation.

I think these cases were also broken before, so I do not want fixing this to block this PR. Just making a note of where the issues are.

modules/gltf/gltf_document.cpp Show resolved Hide resolved
bool last = false;
while (true) {
Quaternion rotation;
Error err = p_godot_animation->try_rotation_track_interpolate(p_godot_anim_track_index, time, &rotation);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both the old and the new code have broken cubic interpolation TYPE_VALUE trs tracks.

This is incorrect if track is TRACK_VALUE:
See scene/resources/animation.cpp:

ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, ERR_INVALID_PARAMETER);

bool last = false;
while (true) {
Vector3 position;
Error err = p_godot_animation->try_position_track_interpolate(p_godot_anim_track_index, time, &position);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both the old and the new code have broken cubic interpolation TYPE_VALUE trs tracks.

ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, ERR_INVALID_PARAMETER);

bool last = false;
while (true) {
Vector3 scale;
Error err = p_godot_animation->try_scale_track_interpolate(p_godot_anim_track_index, time, &scale);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both the old and the new code have broken cubic interpolation TYPE_VALUE trs tracks.

ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, ERR_INVALID_PARAMETER);

err = p_godot_animation->try_rotation_track_interpolate(p_godot_anim_track_index, time, &rotation);
ERR_CONTINUE(err != OK);
err = p_godot_animation->try_scale_track_interpolate(p_godot_anim_track_index, time, &scale);
ERR_CONTINUE(err != OK);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both the old and the new code have broken cubic interpolation TYPE_VALUE trs tracks.

This is incorrect if track is TRACK_VALUE:
See scene/resources/animation.cpp:

ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, ERR_INVALID_PARAMETER);

modules/gltf/gltf_document.cpp Outdated Show resolved Hide resolved
if (p_handle_skeletons && skeleton != -1) {
// Special case for skeleton nodes, skip all bones so that the path is to the Skeleton3D node.
// A path that would otherwise be `A/B/C/Bone1/Bone2/Bone3` becomes `A/B/C/Skeleton3D:Bone3`.
subpath.append(get_name());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test animating blend shapes or materials on a MeshInstance3D that is a child of a BoneAttachment3D (bone) which does not have skin

modules/gltf/structures/gltf_node.cpp Outdated Show resolved Hide resolved
// At this point, we have found a node with the shape index we were looking for.
if (_will_gltf_shape_become_subnode(p_state, gltf_node, node_index)) {
Vector<StringName> sname_path = node_path.get_names();
sname_path.append(gltf_node->get_name() + "Shape");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this causes a name collision, the physics animations will not work as expected, since "Shape" will already be in use.

We should consider reserving node name + "Shape" when importing nodes.

Copy link
Contributor

@lyuma lyuma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed together in a call with @fire and @aaronfranke (took 3 hours!)

This seems great! No major issues. It would be good to get it merged so we can start testing it for imports and exports

const int track_index = animation->get_track_count();
animation->add_track(Animation::TYPE_VALUE);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will cause individually animated .../weights/N tracks to be TYPE_VALUE, but we should convert them to TYPE_BLEND_SHAPE with the name of the blend shape (will have to find the mesh and get the shape name)

@akien-mga akien-mga changed the title GLTF: Implement KHR_animation_pointer for animating custom properties GLTF: Implement KHR_animation_pointer for animating custom properties Oct 4, 2024
modules/gltf/structures/gltf_object_model_property.h Outdated Show resolved Hide resolved
modules/gltf/structures/gltf_animation.cpp Outdated Show resolved Hide resolved
modules/gltf/structures/gltf_node.cpp Outdated Show resolved Hide resolved
modules/gltf/gltf_document.cpp Outdated Show resolved Hide resolved
modules/gltf/gltf_document.cpp Outdated Show resolved Hide resolved
modules/gltf/gltf_document.cpp Outdated Show resolved Hide resolved
modules/gltf/structures/gltf_animation.cpp Outdated Show resolved Hide resolved
modules/gltf/doc_classes/GLTFObjectModelProperty.xml Outdated Show resolved Hide resolved
modules/gltf/gltf_document.cpp Outdated Show resolved Hide resolved
modules/gltf/gltf_document.cpp Outdated Show resolved Hide resolved
@Repiteo
Copy link
Contributor

Repiteo commented Oct 30, 2024

It says in the queue that this is awaiting final touches, but that might be outdated. Is this ready for merge in your eyes @aaronfranke?

@aaronfranke
Copy link
Member Author

@Repiteo There are some minor things, but they can all be resolved after merge. Every remaining problem pointed out above is actually a pre-existing problem, not something introduced by this PR.

I'd personally like to avoid making this PR too much larger, and it would be nice to avoid conflicting with myself too much, so merging this before fixing the other things makes sense. I did leave a note that I suggest merging #96748 first so that we can make UV map animations work correctly. I'll either need to update this PR or that PR.

Copy link
Contributor

@lyuma lyuma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was asked to give another approval. I compared the diffs since last review and they seem fine.

Every remaining problem pointed out above is actually a pre-existing problem, not something introduced by this PR.

To be clear, there are two new problems. They are both minor and not blockers.

  • individually animated .../weights/N tracks to be TYPE_VALUE, but we should convert them to TYPE_BLEND_SHAPE with the name of the blend shape
  • We should consider reserving node name + "Shape" when importing nodes.

I would be happy to see this merged.

@Repiteo
Copy link
Contributor

Repiteo commented Nov 5, 2024

This one has more thorough review than #96748 & a handful of approvals to boot, so that PR can be expanded to handle the minor problems @lyuma mentioned after this is merged

@Repiteo Repiteo merged commit ef8aafc into godotengine:master Nov 5, 2024
20 checks passed
@Repiteo
Copy link
Contributor

Repiteo commented Nov 5, 2024

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Development

Successfully merging this pull request may close these issues.

7 participants