Skip to content

Commit

Permalink
Implement Animation Blend Shape Tracks
Browse files Browse the repository at this point in the history
* New track type BLEND_SHAPE
* Blend shapes are imported via this new track type
* Processing is more optimized (no longer relies on variants)
* Modified the Blend Shape API in MeshInstance3D to use indices rather than StringNames (more optimizes)
* Promo: Fixed a small bug in gizmo updating in Node3D that affected performance

Dedicated BlendShape tracks are required for both optimization and eventually implementing them in animation compression.
  • Loading branch information
reduz committed Oct 16, 2021
1 parent 96410f5 commit ae1c016
Show file tree
Hide file tree
Showing 18 changed files with 736 additions and 103 deletions.
18 changes: 14 additions & 4 deletions doc/classes/Animation.xml
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,14 @@
Sets the value of the key identified by [code]key_idx[/code] to the given value. The [code]track_idx[/code] must be the index of a Bezier Track.
</description>
</method>
<method name="blend_shape_track_insert_key">
<return type="int" />
<argument index="0" name="track_idx" type="int" />
<argument index="1" name="time" type="float" />
<argument index="2" name="amount" type="float" />
<description>
</description>
</method>
<method name="clear">
<return type="void" />
<description>
Expand Down Expand Up @@ -552,16 +560,18 @@
</constant>
<constant name="TYPE_SCALE_3D" value="3" enum="TrackType">
</constant>
<constant name="TYPE_METHOD" value="4" enum="TrackType">
<constant name="TYPE_BLEND_SHAPE" value="4" enum="TrackType">
</constant>
<constant name="TYPE_METHOD" value="5" enum="TrackType">
Method tracks call functions with given arguments per key.
</constant>
<constant name="TYPE_BEZIER" value="5" enum="TrackType">
<constant name="TYPE_BEZIER" value="6" enum="TrackType">
Bezier tracks are used to interpolate a value using custom curves. They can also be used to animate sub-properties of vectors and colors (e.g. alpha value of a [Color]).
</constant>
<constant name="TYPE_AUDIO" value="6" enum="TrackType">
<constant name="TYPE_AUDIO" value="7" enum="TrackType">
Audio tracks are used to play an audio stream with either type of [AudioStreamPlayer]. The stream can be trimmed and previewed in the animation.
</constant>
<constant name="TYPE_ANIMATION" value="7" enum="TrackType">
<constant name="TYPE_ANIMATION" value="8" enum="TrackType">
Animation tracks play animations in other [AnimationPlayer] nodes.
</constant>
<constant name="INTERPOLATION_NEAREST" value="0" enum="InterpolationType">
Expand Down
24 changes: 24 additions & 0 deletions doc/classes/MeshInstance3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,30 @@
This helper creates a [StaticBody3D] child node with a [ConcavePolygonShape3D] collision shape calculated from the mesh geometry. It's mainly used for testing.
</description>
</method>
<method name="find_blend_shape_by_name">
<return type="int" />
<argument index="0" name="name" type="StringName" />
<description>
</description>
</method>
<method name="get_active_material" qualifiers="const">
<return type="Material" />
<argument index="0" name="surface" type="int" />
<description>
Returns the [Material] that will be used by the [Mesh] when drawing. This can return the [member GeometryInstance3D.material_override], the surface override [Material] defined in this [MeshInstance3D], or the surface [Material] defined in the [Mesh]. For example, if [member GeometryInstance3D.material_override] is used, all surfaces will return the override material.
</description>
</method>
<method name="get_blend_shape_count" qualifiers="const">
<return type="int" />
<description>
</description>
</method>
<method name="get_blend_shape_value" qualifiers="const">
<return type="float" />
<argument index="0" name="blend_shape_idx" type="int" />
<description>
</description>
</method>
<method name="get_surface_override_material" qualifiers="const">
<return type="Material" />
<argument index="0" name="surface" type="int" />
Expand All @@ -61,6 +78,13 @@
Returns the number of surface override materials. This is equivalent to [method Mesh.get_surface_count].
</description>
</method>
<method name="set_blend_shape_value">
<return type="void" />
<argument index="0" name="blend_shape_idx" type="int" />
<argument index="1" name="value" type="float" />
<description>
</description>
</method>
<method name="set_surface_override_material">
<return type="void" />
<argument index="0" name="surface" type="int" />
Expand Down
48 changes: 42 additions & 6 deletions editor/animation_track_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ class AnimationTrackKeyEdit : public Object {
}

} break;
case Animation::TYPE_BLEND_SHAPE:
case Animation::TYPE_VALUE: {
if (name == "value") {
Variant value = p_value;
Expand Down Expand Up @@ -438,6 +439,7 @@ class AnimationTrackKeyEdit : public Object {
return true;
}
} break;
case Animation::TYPE_BLEND_SHAPE:
case Animation::TYPE_VALUE: {
if (name == "value") {
r_ret = animation->track_get_key_value(track, key);
Expand Down Expand Up @@ -551,6 +553,9 @@ class AnimationTrackKeyEdit : public Object {
case Animation::TYPE_SCALE_3D: {
p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale"));
} break;
case Animation::TYPE_BLEND_SHAPE: {
p_list->push_back(PropertyInfo(Variant::FLOAT, "value"));
} break;
case Animation::TYPE_VALUE: {
Variant v = animation->track_get_key_value(track, key);

Expand Down Expand Up @@ -828,6 +833,7 @@ class AnimationMultiTrackKeyEdit : public Object {
undo_redo->add_undo_method(animation.ptr(), "track_set_key_value", track, key, old);
update_obj = true;
} break;
case Animation::TYPE_BLEND_SHAPE:
case Animation::TYPE_VALUE: {
if (name == "value") {
Variant value = p_value;
Expand Down Expand Up @@ -1054,6 +1060,7 @@ class AnimationMultiTrackKeyEdit : public Object {
}

} break;
case Animation::TYPE_BLEND_SHAPE:
case Animation::TYPE_VALUE: {
if (name == "value") {
r_ret = animation->track_get_key_value(track, key);
Expand Down Expand Up @@ -1206,6 +1213,9 @@ class AnimationMultiTrackKeyEdit : public Object {
case Animation::TYPE_SCALE_3D: {
p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale"));
} break;
case Animation::TYPE_BLEND_SHAPE: {
p_list->push_back(PropertyInfo(Variant::FLOAT, "value"));
} break;
case Animation::TYPE_VALUE: {
if (same_key_type) {
Variant v = animation->track_get_key_value(first_track, first_key);
Expand Down Expand Up @@ -1404,6 +1414,7 @@ void AnimationTimelineEdit::_notification(int p_what) {
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXPosition"), SNAME("EditorIcons")), TTR("3D Position Track"));
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXRotation"), SNAME("EditorIcons")), TTR("3D Rotation Track"));
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXScale"), SNAME("EditorIcons")), TTR("3D Scale Track"));
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyBlendShape"), SNAME("EditorIcons")), TTR("Blend Shape Track"));
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")), TTR("Call Method Track"));
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")), TTR("Bezier Curve Track"));
add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")), TTR("Audio Playback Track"));
Expand Down Expand Up @@ -1872,11 +1883,12 @@ void AnimationTrackEdit::_notification(int p_what) {
Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
Color color = get_theme_color(SNAME("font_color"), SNAME("Label"));
Ref<Texture2D> type_icons[8] = {
Ref<Texture2D> type_icons[9] = {
get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyTrackPosition"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyTrackRotation"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyTrackScale"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyTrackBlendShape"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")),
Expand Down Expand Up @@ -2067,7 +2079,7 @@ void AnimationTrackEdit::_notification(int p_what) {
interp_mode_rect.position.y = int(get_size().height - icon->get_height()) / 2;
interp_mode_rect.size = icon->get_size();

if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_BLEND_SHAPE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
draw_texture(icon, interp_mode_rect.position);
}
// Make it easier to click.
Expand All @@ -2077,7 +2089,7 @@ void AnimationTrackEdit::_notification(int p_what) {
ofs += icon->get_width() + hsep;
interp_mode_rect.size.x += hsep;

if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_BLEND_SHAPE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
interp_mode_rect.size.x += down_icon->get_width();
} else {
Expand All @@ -2100,7 +2112,7 @@ void AnimationTrackEdit::_notification(int p_what) {
loop_mode_rect.position.y = int(get_size().height - icon->get_height()) / 2;
loop_mode_rect.size = icon->get_size();

if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_BLEND_SHAPE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
draw_texture(icon, loop_mode_rect.position);
}

Expand All @@ -2110,7 +2122,7 @@ void AnimationTrackEdit::_notification(int p_what) {
ofs += icon->get_width() + hsep;
loop_mode_rect.size.x += hsep;

if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
if (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_BLEND_SHAPE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D) {
draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
loop_mode_rect.size.x += down_icon->get_width();
} else {
Expand Down Expand Up @@ -2330,11 +2342,12 @@ void AnimationTrackEdit::set_animation_and_track(const Ref<Animation> &p_animati
track = p_track;
update();

Ref<Texture2D> type_icons[8] = {
Ref<Texture2D> type_icons[9] = {
get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyXPosition"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyXRotation"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyXScale"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyBlendShape"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")),
get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")),
Expand Down Expand Up @@ -2516,6 +2529,10 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const {
Vector3 t = animation->track_get_key_value(track, key_idx);
text += "Scale: " + String(t) + "\n";
} break;
case Animation::TYPE_BLEND_SHAPE: {
float t = animation->track_get_key_value(track, key_idx);
text += "Blend Shape: " + itos(t) + "\n";
} break;
case Animation::TYPE_VALUE: {
const Variant &v = animation->track_get_key_value(track, key_idx);
text += "Type: " + Variant::get_type_name(v.get_type()) + "\n";
Expand Down Expand Up @@ -3369,6 +3386,8 @@ static bool track_type_is_resettable(Animation::TrackType p_type) {
switch (p_type) {
case Animation::TYPE_VALUE:
[[fallthrough]];
case Animation::TYPE_BLEND_SHAPE:
[[fallthrough]];
case Animation::TYPE_BEZIER:
[[fallthrough]];
case Animation::TYPE_POSITION_3D:
Expand Down Expand Up @@ -4049,6 +4068,7 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD
case Animation::TYPE_POSITION_3D:
case Animation::TYPE_ROTATION_3D:
case Animation::TYPE_SCALE_3D:
case Animation::TYPE_BLEND_SHAPE:
case Animation::TYPE_VALUE: {
value = p_id.value;

Expand Down Expand Up @@ -4468,6 +4488,11 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
ERR_FAIL_COND(!node);
NodePath path_to = root->get_path_to(node);

if (adding_track_type == Animation::TYPE_BLEND_SHAPE && !node->is_class("MeshInstance3D")) {
EditorNode::get_singleton()->show_warning(TTR("Blend Shape tracks only apply to MeshInstance3D nodes."));
return;
}

if ((adding_track_type == Animation::TYPE_POSITION_3D || adding_track_type == Animation::TYPE_ROTATION_3D || adding_track_type == Animation::TYPE_SCALE_3D) && !node->is_class("Node3D")) {
EditorNode::get_singleton()->show_warning(TTR("Position/Rotation/Scale 3D tracks only apply to 3D-based nodes."));
return;
Expand All @@ -4479,6 +4504,13 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
prop_selector->set_type_filter(Vector<Variant::Type>());
prop_selector->select_property_from_instance(node);
} break;
case Animation::TYPE_BLEND_SHAPE: {
adding_track_path = path_to;
Vector<Variant::Type> filter;
filter.push_back(Variant::FLOAT);
prop_selector->set_type_filter(filter);
prop_selector->select_property_from_instance(node);
} break;
case Animation::TYPE_POSITION_3D:
case Animation::TYPE_ROTATION_3D:
case Animation::TYPE_SCALE_3D:
Expand Down Expand Up @@ -4710,6 +4742,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
undo_redo->commit_action();

} break;
case Animation::TYPE_BLEND_SHAPE:
case Animation::TYPE_VALUE: {
NodePath bp;
Variant value;
Expand Down Expand Up @@ -5388,6 +5421,9 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
case Animation::TYPE_SCALE_3D:
text += " (Scale)";
break;
case Animation::TYPE_BLEND_SHAPE:
text += " (BlendShape)";
break;
case Animation::TYPE_METHOD:
text += " (Methods)";
break;
Expand Down
44 changes: 44 additions & 0 deletions editor/icons/KeyBlendShape.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 45 additions & 0 deletions editor/icons/KeyTrackBlendShape.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions editor/import/editor_import_collada.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1710,7 +1710,7 @@ void ColladaImport::create_animation(int p_clip, bool p_import_value_tracks) {
NodeMap &nm = node_map[at.target];
String path = scene->get_path_to(nm.node);

animation->add_track(Animation::TYPE_VALUE);
animation->add_track(Animation::TYPE_BLEND_SHAPE);
int track = animation->get_track_count() - 1;

path = path + ":" + at.param;
Expand All @@ -1732,7 +1732,7 @@ void ColladaImport::create_animation(int p_clip, bool p_import_value_tracks) {
WARN_PRINT("Collada: Unexpected amount of value keys: " + itos(data.size()));
}

animation->track_insert_key(track, time, value);
animation->blend_shape_track_insert_key(track, time, value);
}

tracks_found = true;
Expand Down
Loading

0 comments on commit ae1c016

Please sign in to comment.