Implement Animation Blend Shape Tracks
* 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.
This commit is contained in:
parent
96410f55b2
commit
ae1c016547
|
@ -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>
|
||||
|
@ -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">
|
||||
|
|
|
@ -41,6 +41,12 @@
|
|||
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" />
|
||||
|
@ -48,6 +54,17 @@
|
|||
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" />
|
||||
|
@ -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" />
|
||||
|
|
|
@ -198,6 +198,7 @@ public:
|
|||
}
|
||||
|
||||
} break;
|
||||
case Animation::TYPE_BLEND_SHAPE:
|
||||
case Animation::TYPE_VALUE: {
|
||||
if (name == "value") {
|
||||
Variant value = p_value;
|
||||
|
@ -438,6 +439,7 @@ public:
|
|||
return true;
|
||||
}
|
||||
} break;
|
||||
case Animation::TYPE_BLEND_SHAPE:
|
||||
case Animation::TYPE_VALUE: {
|
||||
if (name == "value") {
|
||||
r_ret = animation->track_get_key_value(track, key);
|
||||
|
@ -551,6 +553,9 @@ public:
|
|||
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);
|
||||
|
||||
|
@ -828,6 +833,7 @@ public:
|
|||
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;
|
||||
|
@ -1054,6 +1060,7 @@ public:
|
|||
}
|
||||
|
||||
} break;
|
||||
case Animation::TYPE_BLEND_SHAPE:
|
||||
case Animation::TYPE_VALUE: {
|
||||
if (name == "value") {
|
||||
r_ret = animation->track_get_key_value(track, key);
|
||||
|
@ -1206,6 +1213,9 @@ public:
|
|||
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);
|
||||
|
@ -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"));
|
||||
|
@ -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")),
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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")),
|
||||
|
@ -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";
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="10"
|
||||
viewBox="0 0 10 10"
|
||||
width="10"
|
||||
version="1.1"
|
||||
id="svg80"
|
||||
sodipodi:docname="KeyBlendShape.svg"
|
||||
inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs84" />
|
||||
<sodipodi:namedview
|
||||
id="namedview82"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="84.4"
|
||||
inkscape:cx="2.6599526"
|
||||
inkscape:cy="5.0059242"
|
||||
inkscape:window-width="1848"
|
||||
inkscape:window-height="1016"
|
||||
inkscape:window-x="72"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg80" />
|
||||
<rect
|
||||
fill="#3cf34e"
|
||||
height="6.1027"
|
||||
ry=".76286"
|
||||
transform="matrix(.70710678 -.70710678 .70710678 .70710678 0 -1042.4)"
|
||||
width="6.1027"
|
||||
x="-740.13947"
|
||||
y="741.10779"
|
||||
id="rect78"
|
||||
style="fill:#5ad5c4;fill-opacity:1" />
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="10"
|
||||
viewBox="0 0 10 10"
|
||||
width="10"
|
||||
version="1.1"
|
||||
id="svg12"
|
||||
sodipodi:docname="KeyTrackBlendShape.svg"
|
||||
inkscape:version="1.1 (1:1.1+202106031931+af4d65493e)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs16" />
|
||||
<sodipodi:namedview
|
||||
id="namedview14"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="29.839906"
|
||||
inkscape:cx="-3.5690461"
|
||||
inkscape:cy="9.0985541"
|
||||
inkscape:window-width="1848"
|
||||
inkscape:window-height="1016"
|
||||
inkscape:window-x="72"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg12" />
|
||||
<ellipse
|
||||
style="fill:none;fill-opacity:0.401212;stroke:none;stroke-width:4.7811;stroke-linejoin:round"
|
||||
id="path921"
|
||||
cx="-0.88986981"
|
||||
cy="6.0959954"
|
||||
rx="1.2495773"
|
||||
ry="1.0867468" />
|
||||
<path
|
||||
id="path1910"
|
||||
style="color:#000000;fill:#5ad5c4;stroke-linejoin:round;-inkscape-stroke:none"
|
||||
d="m 4.5230825,1.1341567 c -2.1310744,0.017055 -3.86718737,1.7635044 -3.86718737,3.8984375 0,1.8778511 1.34348597,3.4523891 3.11718737,3.8164061 L 3.95277,7.5794693 C 2.7929991,7.3095662 1.9351921,6.2780435 1.9351921,5.0325942 c 0,-1.4262775 1.123493,-2.5732858 2.5390622,-2.6152344 v 0.017578 h 0.2011719 l 0.1796875,-1.28125 H 4.5230825 v -0.011719 c 0,-0.00263 -2.64e-5,-0.00518 0,-0.00781 z m 1.6816406,0.019531 -0.1796875,1.28125 h 1.3085937 c 0.078866,0 0.1230469,0.044181 0.1230469,0.1230469 v 4.9882815 c 0,0.07887 -0.044181,0.121093 -0.1230469,0.121094 H 5.2887075 L 5.10902,8.9486103 h 2.2246093 c 0.7663818,0 1.4042969,-0.635962 1.4042969,-1.402344 V 2.5579848 c 0,-0.7663818 -0.637915,-1.4042969 -1.4042969,-1.4042969 z" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
|
@ -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;
|
||||
|
@ -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;
|
||||
|
|
|
@ -1036,6 +1036,10 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
|
|||
} else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
|
||||
Variant var = default_anim->value_track_interpolate(j, from);
|
||||
new_anim->track_insert_key(dtrack, 0, var);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {
|
||||
float interp;
|
||||
default_anim->blend_shape_track_interpolate(j, from, &interp);
|
||||
new_anim->blend_shape_track_insert_key(dtrack, 0, interp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1055,6 +1059,10 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
|
|||
} else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
|
||||
Variant var = default_anim->track_get_key_value(j, k);
|
||||
new_anim->track_insert_key(dtrack, kt - from, var);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {
|
||||
float interp;
|
||||
default_anim->blend_shape_track_get_key(j, k, &interp);
|
||||
new_anim->blend_shape_track_insert_key(dtrack, kt - from, interp);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1074,6 +1082,10 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
|
|||
} else if (default_anim->track_get_type(j) == Animation::TYPE_VALUE) {
|
||||
Variant var = default_anim->value_track_interpolate(j, to);
|
||||
new_anim->track_insert_key(dtrack, to - from, var);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {
|
||||
float interp;
|
||||
default_anim->blend_shape_track_interpolate(j, to, &interp);
|
||||
new_anim->blend_shape_track_insert_key(dtrack, to - from, interp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1105,6 +1117,12 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
|
|||
new_anim->track_insert_key(dtrack, 0, var);
|
||||
Variant to_var = default_anim->value_track_interpolate(j, to);
|
||||
new_anim->track_insert_key(dtrack, to - from, to_var);
|
||||
} else if (default_anim->track_get_type(j) == Animation::TYPE_BLEND_SHAPE) {
|
||||
float interp;
|
||||
default_anim->blend_shape_track_interpolate(j, from, &interp);
|
||||
new_anim->blend_shape_track_insert_key(dtrack, 0, interp);
|
||||
default_anim->blend_shape_track_interpolate(j, to, &interp);
|
||||
new_anim->blend_shape_track_insert_key(dtrack, to - from, interp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1652,7 +1670,7 @@ void ResourceImporterScene::_optimize_track_usage(AnimationPlayer *p_player, Ani
|
|||
ERR_FAIL_COND(parent == nullptr);
|
||||
OrderedHashMap<NodePath, uint32_t> used_tracks[TRACK_CHANNEL_MAX];
|
||||
bool tracks_to_add = false;
|
||||
static const Animation::TrackType track_types[TRACK_CHANNEL_MAX] = { Animation::TYPE_POSITION_3D, Animation::TYPE_ROTATION_3D, Animation::TYPE_SCALE_3D };
|
||||
static const Animation::TrackType track_types[TRACK_CHANNEL_MAX] = { Animation::TYPE_POSITION_3D, Animation::TYPE_ROTATION_3D, Animation::TYPE_SCALE_3D, Animation::TYPE_BLEND_SHAPE };
|
||||
for (const StringName &I : anims) {
|
||||
Ref<Animation> anim = p_player->get_animation(I);
|
||||
for (int i = 0; i < anim->get_track_count(); i++) {
|
||||
|
@ -1710,67 +1728,84 @@ void ResourceImporterScene::_optimize_track_usage(AnimationPlayer *p_player, Ani
|
|||
|
||||
NodePath path = J.key();
|
||||
Node *n = parent->get_node(path);
|
||||
Skeleton3D *skel = Object::cast_to<Skeleton3D>(n);
|
||||
Node3D *n3d = Object::cast_to<Node3D>(n);
|
||||
Vector3 loc;
|
||||
Quaternion rot;
|
||||
Vector3 scale;
|
||||
if (skel && path.get_subname_count() > 0) {
|
||||
StringName bone = path.get_subname(0);
|
||||
int bone_idx = skel->find_bone(bone);
|
||||
if (bone_idx == -1) {
|
||||
|
||||
if (j == TRACK_CHANNEL_BLEND_SHAPE) {
|
||||
MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(n);
|
||||
if (mi && path.get_subname_count() > 0) {
|
||||
StringName bs = path.get_subname(0);
|
||||
bool valid;
|
||||
float value = mi->get(bs, &valid);
|
||||
if (valid) {
|
||||
int track_idx = anim->add_track(track_types[j]);
|
||||
anim->track_set_path(track_idx, path);
|
||||
anim->track_set_imported(track_idx, true);
|
||||
anim->blend_shape_track_insert_key(track_idx, 0, value);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
Skeleton3D *skel = Object::cast_to<Skeleton3D>(n);
|
||||
Node3D *n3d = Object::cast_to<Node3D>(n);
|
||||
Vector3 loc;
|
||||
Quaternion rot;
|
||||
Vector3 scale;
|
||||
if (skel && path.get_subname_count() > 0) {
|
||||
StringName bone = path.get_subname(0);
|
||||
int bone_idx = skel->find_bone(bone);
|
||||
if (bone_idx == -1) {
|
||||
continue;
|
||||
}
|
||||
skel->get_bone_pose(bone_idx);
|
||||
loc = skel->get_bone_pose_position(bone_idx);
|
||||
rot = skel->get_bone_pose_rotation(bone_idx);
|
||||
scale = skel->get_bone_pose_scale(bone_idx);
|
||||
} else if (n3d) {
|
||||
loc = n3d->get_position();
|
||||
rot = n3d->get_transform().basis.get_rotation_quaternion();
|
||||
scale = n3d->get_scale();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
skel->get_bone_pose(bone_idx);
|
||||
loc = skel->get_bone_pose_position(bone_idx);
|
||||
rot = skel->get_bone_pose_rotation(bone_idx);
|
||||
scale = skel->get_bone_pose_scale(bone_idx);
|
||||
} else if (n3d) {
|
||||
loc = n3d->get_position();
|
||||
rot = n3d->get_transform().basis.get_rotation_quaternion();
|
||||
scale = n3d->get_scale();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure insertion keeps tracks together and ordered by type (loc/rot/scale)
|
||||
int insert_at_pos = -1;
|
||||
for (int k = 0; k < anim->get_track_count(); k++) {
|
||||
NodePath tpath = anim->track_get_path(k);
|
||||
// Ensure insertion keeps tracks together and ordered by type (loc/rot/scale)
|
||||
int insert_at_pos = -1;
|
||||
for (int k = 0; k < anim->get_track_count(); k++) {
|
||||
NodePath tpath = anim->track_get_path(k);
|
||||
|
||||
if (path == tpath) {
|
||||
Animation::TrackType ttype = anim->track_get_type(k);
|
||||
if (insert_at_pos == -1) {
|
||||
// First insert, determine whether replacing or kicking back
|
||||
if (track_types[j] < ttype) {
|
||||
insert_at_pos = k;
|
||||
break; // No point in continuing.
|
||||
} else {
|
||||
if (path == tpath) {
|
||||
Animation::TrackType ttype = anim->track_get_type(k);
|
||||
if (insert_at_pos == -1) {
|
||||
// First insert, determine whether replacing or kicking back
|
||||
if (track_types[j] < ttype) {
|
||||
insert_at_pos = k;
|
||||
break; // No point in continuing.
|
||||
} else {
|
||||
insert_at_pos = k + 1;
|
||||
}
|
||||
} else if (ttype < track_types[j]) {
|
||||
// Kick back.
|
||||
insert_at_pos = k + 1;
|
||||
}
|
||||
} else if (ttype < track_types[j]) {
|
||||
// Kick back.
|
||||
insert_at_pos = k + 1;
|
||||
} else if (insert_at_pos >= 0) {
|
||||
break;
|
||||
}
|
||||
} else if (insert_at_pos >= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
int track_idx = anim->add_track(track_types[j], insert_at_pos);
|
||||
int track_idx = anim->add_track(track_types[j], insert_at_pos);
|
||||
|
||||
anim->track_set_path(track_idx, path);
|
||||
anim->track_set_imported(track_idx, true);
|
||||
switch (j) {
|
||||
case TRACK_CHANNEL_POSITION: {
|
||||
anim->position_track_insert_key(track_idx, 0, loc);
|
||||
} break;
|
||||
case TRACK_CHANNEL_ROTATION: {
|
||||
anim->rotation_track_insert_key(track_idx, 0, rot);
|
||||
} break;
|
||||
case TRACK_CHANNEL_SCALE: {
|
||||
anim->scale_track_insert_key(track_idx, 0, scale);
|
||||
} break;
|
||||
default: {
|
||||
anim->track_set_path(track_idx, path);
|
||||
anim->track_set_imported(track_idx, true);
|
||||
switch (j) {
|
||||
case TRACK_CHANNEL_POSITION: {
|
||||
anim->position_track_insert_key(track_idx, 0, loc);
|
||||
} break;
|
||||
case TRACK_CHANNEL_ROTATION: {
|
||||
anim->rotation_track_insert_key(track_idx, 0, rot);
|
||||
} break;
|
||||
case TRACK_CHANNEL_SCALE: {
|
||||
anim->scale_track_insert_key(track_idx, 0, scale);
|
||||
} break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -207,6 +207,7 @@ class ResourceImporterScene : public ResourceImporter {
|
|||
TRACK_CHANNEL_POSITION,
|
||||
TRACK_CHANNEL_ROTATION,
|
||||
TRACK_CHANNEL_SCALE,
|
||||
TRACK_CHANNEL_BLEND_SHAPE,
|
||||
TRACK_CHANNEL_MAX
|
||||
};
|
||||
|
||||
|
|
|
@ -5991,12 +5991,11 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
|
|||
ERR_CONTINUE(mesh.is_null());
|
||||
ERR_CONTINUE(mesh->get_mesh().is_null());
|
||||
ERR_CONTINUE(mesh->get_mesh()->get_mesh().is_null());
|
||||
const String prop = "blend_shapes/" + mesh->get_mesh()->get_blend_shape_name(i);
|
||||
|
||||
const String blend_path = String(node_path) + ":" + prop;
|
||||
const String blend_path = String(node_path) + ":" + String(mesh->get_mesh()->get_blend_shape_name(i));
|
||||
|
||||
const int track_idx = animation->get_track_count();
|
||||
animation->add_track(Animation::TYPE_VALUE);
|
||||
animation->add_track(Animation::TYPE_BLEND_SHAPE);
|
||||
animation->track_set_path(track_idx, blend_path);
|
||||
|
||||
// Only LINEAR and STEP (NEAREST) can be supported out of the box by Godot's Animation,
|
||||
|
@ -6007,7 +6006,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
|
|||
for (int j = 0; j < track.weight_tracks[i].times.size(); j++) {
|
||||
const float t = track.weight_tracks[i].times[j];
|
||||
const float attribs = track.weight_tracks[i].values[j];
|
||||
animation->track_insert_key(track_idx, t, attribs);
|
||||
animation->blend_shape_track_insert_key(track_idx, t, attribs);
|
||||
}
|
||||
} else {
|
||||
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
|
||||
|
@ -6015,7 +6014,8 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
|
|||
double time = 0.0;
|
||||
bool last = false;
|
||||
while (true) {
|
||||
_interpolate_track<float>(track.weight_tracks[i].times, track.weight_tracks[i].values, time, gltf_interp);
|
||||
float blend = _interpolate_track<float>(track.weight_tracks[i].times, track.weight_tracks[i].values, time, gltf_interp);
|
||||
animation->blend_shape_track_insert_key(track_idx, time, blend);
|
||||
if (last) {
|
||||
break;
|
||||
}
|
||||
|
@ -6459,7 +6459,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
|
|||
if (!tracks.has(mesh_index)) {
|
||||
for (int32_t shape_i = 0; shape_i < mesh->get_blend_shape_count(); shape_i++) {
|
||||
String shape_name = mesh->get_blend_shape_name(shape_i);
|
||||
NodePath shape_path = String(path) + ":blend_shapes/" + shape_name;
|
||||
NodePath shape_path = String(path) + ":" + shape_name;
|
||||
int32_t shape_track_i = animation->find_track(shape_path);
|
||||
if (shape_track_i == -1) {
|
||||
GLTFAnimation::Channel<float> weight;
|
||||
|
|
|
@ -42,10 +42,9 @@ bool MeshInstance3D::_set(const StringName &p_name, const Variant &p_value) {
|
|||
return false;
|
||||
}
|
||||
|
||||
Map<StringName, BlendShapeTrack>::Element *E = blend_shape_tracks.find(p_name);
|
||||
Map<StringName, int>::Element *E = blend_shape_properties.find(p_name);
|
||||
if (E) {
|
||||
E->get().value = p_value;
|
||||
RenderingServer::get_singleton()->instance_set_blend_shape_weight(get_instance(), E->get().idx, E->get().value);
|
||||
set_blend_shape_value(E->get(), p_value);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -67,9 +66,9 @@ bool MeshInstance3D::_get(const StringName &p_name, Variant &r_ret) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
const Map<StringName, BlendShapeTrack>::Element *E = blend_shape_tracks.find(p_name);
|
||||
const Map<StringName, int>::Element *E = blend_shape_properties.find(p_name);
|
||||
if (E) {
|
||||
r_ret = E->get().value;
|
||||
r_ret = get_blend_shape_value(E->get());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -86,7 +85,7 @@ bool MeshInstance3D::_get(const StringName &p_name, Variant &r_ret) const {
|
|||
|
||||
void MeshInstance3D::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
List<String> ls;
|
||||
for (const KeyValue<StringName, BlendShapeTrack> &E : blend_shape_tracks) {
|
||||
for (const KeyValue<StringName, int> &E : blend_shape_properties) {
|
||||
ls.push_back(E.key);
|
||||
}
|
||||
|
||||
|
@ -114,25 +113,17 @@ void MeshInstance3D::set_mesh(const Ref<Mesh> &p_mesh) {
|
|||
|
||||
mesh = p_mesh;
|
||||
|
||||
blend_shape_tracks.clear();
|
||||
if (mesh.is_valid()) {
|
||||
for (int i = 0; i < mesh->get_blend_shape_count(); i++) {
|
||||
BlendShapeTrack mt;
|
||||
mt.idx = i;
|
||||
mt.value = 0;
|
||||
blend_shape_tracks["blend_shapes/" + String(mesh->get_blend_shape_name(i))] = mt;
|
||||
}
|
||||
|
||||
mesh->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &MeshInstance3D::_mesh_changed));
|
||||
surface_override_materials.resize(mesh->get_surface_count());
|
||||
|
||||
_mesh_changed();
|
||||
set_base(mesh->get_rid());
|
||||
} else {
|
||||
blend_shape_tracks.clear();
|
||||
blend_shape_properties.clear();
|
||||
set_base(RID());
|
||||
update_gizmos();
|
||||
}
|
||||
|
||||
update_gizmos();
|
||||
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
|
@ -140,6 +131,35 @@ Ref<Mesh> MeshInstance3D::get_mesh() const {
|
|||
return mesh;
|
||||
}
|
||||
|
||||
int MeshInstance3D::get_blend_shape_count() const {
|
||||
if (mesh.is_null()) {
|
||||
return 0;
|
||||
}
|
||||
return mesh->get_blend_shape_count();
|
||||
}
|
||||
int MeshInstance3D::find_blend_shape_by_name(const StringName &p_name) {
|
||||
if (mesh.is_null()) {
|
||||
return -1;
|
||||
}
|
||||
for (int i = 0; i < mesh->get_blend_shape_count(); i++) {
|
||||
if (mesh->get_blend_shape_name(i) == p_name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
float MeshInstance3D::get_blend_shape_value(int p_blend_shape) const {
|
||||
ERR_FAIL_COND_V(mesh.is_null(), 0.0);
|
||||
ERR_FAIL_INDEX_V(p_blend_shape, (int)blend_shape_tracks.size(), 0);
|
||||
return blend_shape_tracks[p_blend_shape];
|
||||
}
|
||||
void MeshInstance3D::set_blend_shape_value(int p_blend_shape, float p_value) {
|
||||
ERR_FAIL_COND(mesh.is_null());
|
||||
ERR_FAIL_INDEX(p_blend_shape, (int)blend_shape_tracks.size());
|
||||
blend_shape_tracks[p_blend_shape] = p_value;
|
||||
RenderingServer::get_singleton()->instance_set_blend_shape_weight(get_instance(), p_blend_shape, p_value);
|
||||
}
|
||||
|
||||
void MeshInstance3D::_resolve_skeleton_path() {
|
||||
Ref<SkinReference> new_skin_reference;
|
||||
|
||||
|
@ -357,6 +377,19 @@ Ref<Material> MeshInstance3D::get_active_material(int p_surface) const {
|
|||
void MeshInstance3D::_mesh_changed() {
|
||||
ERR_FAIL_COND(mesh.is_null());
|
||||
surface_override_materials.resize(mesh->get_surface_count());
|
||||
|
||||
uint32_t initialize_bs_from = blend_shape_tracks.size();
|
||||
blend_shape_tracks.resize(mesh->get_blend_shape_count());
|
||||
|
||||
for (uint32_t i = 0; i < blend_shape_tracks.size(); i++) {
|
||||
blend_shape_properties["blend_shapes/" + String(mesh->get_blend_shape_name(i))] = i;
|
||||
if (i < initialize_bs_from) {
|
||||
set_blend_shape_value(i, blend_shape_tracks[i]);
|
||||
} else {
|
||||
set_blend_shape_value(i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
update_gizmos();
|
||||
}
|
||||
|
||||
|
@ -459,6 +492,11 @@ void MeshInstance3D::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("create_multiple_convex_collisions"), &MeshInstance3D::create_multiple_convex_collisions);
|
||||
ClassDB::set_method_flags("MeshInstance3D", "create_multiple_convex_collisions", METHOD_FLAGS_DEFAULT);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_blend_shape_count"), &MeshInstance3D::get_blend_shape_count);
|
||||
ClassDB::bind_method(D_METHOD("find_blend_shape_by_name", "name"), &MeshInstance3D::find_blend_shape_by_name);
|
||||
ClassDB::bind_method(D_METHOD("get_blend_shape_value", "blend_shape_idx"), &MeshInstance3D::get_blend_shape_value);
|
||||
ClassDB::bind_method(D_METHOD("set_blend_shape_value", "blend_shape_idx", "value"), &MeshInstance3D::set_blend_shape_value);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("create_debug_tangents"), &MeshInstance3D::create_debug_tangents);
|
||||
ClassDB::set_method_flags("MeshInstance3D", "create_debug_tangents", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
|
||||
|
||||
|
|
|
@ -31,8 +31,8 @@
|
|||
#ifndef MESH_INSTANCE_H
|
||||
#define MESH_INSTANCE_H
|
||||
|
||||
#include "core/templates/local_vector.h"
|
||||
#include "scene/3d/visual_instance_3d.h"
|
||||
|
||||
class Skin;
|
||||
class SkinReference;
|
||||
|
||||
|
@ -46,12 +46,8 @@ protected:
|
|||
Ref<SkinReference> skin_ref;
|
||||
NodePath skeleton_path = NodePath("..");
|
||||
|
||||
struct BlendShapeTrack {
|
||||
int idx = 0;
|
||||
float value = 0.0;
|
||||
};
|
||||
|
||||
Map<StringName, BlendShapeTrack> blend_shape_tracks;
|
||||
LocalVector<float> blend_shape_tracks;
|
||||
Map<StringName, int> blend_shape_properties;
|
||||
Vector<Ref<Material>> surface_override_materials;
|
||||
|
||||
void _mesh_changed();
|
||||
|
@ -75,6 +71,11 @@ public:
|
|||
void set_skeleton_path(const NodePath &p_skeleton);
|
||||
NodePath get_skeleton_path();
|
||||
|
||||
int get_blend_shape_count() const;
|
||||
int find_blend_shape_by_name(const StringName &p_name);
|
||||
float get_blend_shape_value(int p_blend_shape) const;
|
||||
void set_blend_shape_value(int p_blend_shape, float p_value);
|
||||
|
||||
int get_surface_override_material_count() const;
|
||||
void set_surface_override_material(int p_surface, const Ref<Material> &p_material);
|
||||
Ref<Material> get_surface_override_material(int p_surface) const;
|
||||
|
|
|
@ -371,6 +371,9 @@ void Node3D::update_gizmos() {
|
|||
if (data.gizmos.is_empty()) {
|
||||
return;
|
||||
}
|
||||
if (data.gizmos_dirty) {
|
||||
return;
|
||||
}
|
||||
data.gizmos_dirty = true;
|
||||
MessageQueue::get_singleton()->push_callable(callable_mp(this, &Node3D::_update_gizmos));
|
||||
#endif
|
||||
|
@ -467,6 +470,7 @@ Vector<Ref<Node3DGizmo>> Node3D::get_gizmos() const {
|
|||
void Node3D::_update_gizmos() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (data.gizmos_disabled || !is_inside_world() || !data.gizmos_dirty) {
|
||||
data.gizmos_dirty = false;
|
||||
return;
|
||||
}
|
||||
data.gizmos_dirty = false;
|
||||
|
|
|
@ -257,6 +257,7 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
|
|||
ERR_CONTINUE_MSG(!child, "On Animation: '" + p_anim->name + "', couldn't resolve track: '" + String(a->track_get_path(i)) + "'."); // couldn't find the child node
|
||||
ObjectID id = resource.is_valid() ? resource->get_instance_id() : child->get_instance_id();
|
||||
int bone_idx = -1;
|
||||
int blend_shape_idx = -1;
|
||||
|
||||
#ifndef _3D_DISABLED
|
||||
if (a->track_get_path(i).get_subname_count() == 1 && Object::cast_to<Skeleton3D>(child)) {
|
||||
|
@ -266,6 +267,22 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
|
|||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (a->track_get_type(i) == Animation::TYPE_BLEND_SHAPE) {
|
||||
MeshInstance3D *mi_3d = Object::cast_to<MeshInstance3D>(child);
|
||||
if (!mi_3d) {
|
||||
continue;
|
||||
}
|
||||
if (a->track_get_path(i).get_subname_count() != 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
blend_shape_idx = mi_3d->find_blend_shape_by_name(a->track_get_path(i).get_subname(0));
|
||||
if (blend_shape_idx == -1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _3D_DISABLED
|
||||
|
||||
{
|
||||
|
@ -277,6 +294,7 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
|
|||
TrackNodeCacheKey key;
|
||||
key.id = id;
|
||||
key.bone_idx = bone_idx;
|
||||
key.blend_shape_idx = blend_shape_idx;
|
||||
|
||||
if (!node_cache_map.has(key)) {
|
||||
node_cache_map[key] = TrackNodeCache();
|
||||
|
@ -334,6 +352,13 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (a->track_get_type(i) == Animation::TYPE_BLEND_SHAPE) {
|
||||
// special cases and caches for transform tracks
|
||||
node_cache->node_blend_shape = Object::cast_to<MeshInstance3D>(child);
|
||||
node_cache->blend_shape_idx = blend_shape_idx;
|
||||
}
|
||||
|
||||
#endif // _3D_DISABLED
|
||||
|
||||
if (a->track_get_type(i) == Animation::TYPE_VALUE) {
|
||||
|
@ -479,6 +504,31 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
|
|||
} else {
|
||||
nc->scale_accum = nc->scale_accum.lerp(scale, p_interp);
|
||||
}
|
||||
#endif // _3D_DISABLED
|
||||
} break;
|
||||
case Animation::TYPE_BLEND_SHAPE: {
|
||||
#ifndef _3D_DISABLED
|
||||
if (!nc->node_blend_shape) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float blend;
|
||||
|
||||
Error err = a->blend_shape_track_interpolate(i, p_time, &blend);
|
||||
//ERR_CONTINUE(err!=OK); //used for testing, should be removed
|
||||
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nc->accum_pass != accum_pass) {
|
||||
ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX);
|
||||
nc->accum_pass = accum_pass;
|
||||
cache_update[cache_update_size++] = nc;
|
||||
nc->blend_shape_accum = blend;
|
||||
} else {
|
||||
nc->blend_shape_accum = Math::lerp(nc->blend_shape_accum, blend, p_interp);
|
||||
}
|
||||
#endif // _3D_DISABLED
|
||||
} break;
|
||||
case Animation::TYPE_VALUE: {
|
||||
|
@ -947,6 +997,8 @@ void AnimationPlayer::_animation_update_transforms() {
|
|||
nc->skeleton->set_bone_pose_scale(nc->bone_idx, nc->scale_accum);
|
||||
}
|
||||
|
||||
} else if (nc->node_blend_shape) {
|
||||
nc->node_blend_shape->set_blend_shape_value(nc->blend_shape_idx, nc->blend_shape_accum);
|
||||
} else if (nc->node_3d) {
|
||||
if (nc->loc_used) {
|
||||
nc->node_3d->set_position(nc->loc_accum);
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#define ANIMATION_PLAYER_H
|
||||
|
||||
#include "scene/2d/node_2d.h"
|
||||
#include "scene/3d/mesh_instance_3d.h"
|
||||
#include "scene/3d/node_3d.h"
|
||||
#include "scene/3d/skeleton_3d.h"
|
||||
#include "scene/resources/animation.h"
|
||||
|
@ -99,6 +100,8 @@ private:
|
|||
#ifndef _3D_DISABLED
|
||||
Node3D *node_3d = nullptr;
|
||||
Skeleton3D *skeleton = nullptr;
|
||||
MeshInstance3D *node_blend_shape = nullptr;
|
||||
int blend_shape_idx = -1;
|
||||
#endif // _3D_DISABLED
|
||||
int bone_idx = -1;
|
||||
// accumulated transforms
|
||||
|
@ -110,6 +113,7 @@ private:
|
|||
Vector3 loc_accum;
|
||||
Quaternion rot_accum;
|
||||
Vector3 scale_accum;
|
||||
float blend_shape_accum = 0;
|
||||
uint64_t accum_pass = 0;
|
||||
|
||||
bool audio_playing = false;
|
||||
|
@ -147,10 +151,15 @@ private:
|
|||
struct TrackNodeCacheKey {
|
||||
ObjectID id;
|
||||
int bone_idx = -1;
|
||||
int blend_shape_idx = -1;
|
||||
|
||||
inline bool operator<(const TrackNodeCacheKey &p_right) const {
|
||||
if (id == p_right.id) {
|
||||
return bone_idx < p_right.bone_idx;
|
||||
if (blend_shape_idx == p_right.blend_shape_idx) {
|
||||
return bone_idx < p_right.bone_idx;
|
||||
} else {
|
||||
return blend_shape_idx < p_right.blend_shape_idx;
|
||||
}
|
||||
} else {
|
||||
return id < p_right.id;
|
||||
}
|
||||
|
|
|
@ -639,6 +639,37 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
|
|||
}
|
||||
|
||||
#endif // _3D_DISABLED
|
||||
} break;
|
||||
case Animation::TYPE_BLEND_SHAPE: {
|
||||
#ifndef _3D_DISABLED
|
||||
|
||||
if (path.get_subname_count() != 1) {
|
||||
ERR_PRINT("AnimationTree: '" + String(E) + "', blend shape track does not contain a blend shape subname: '" + String(path) + "'");
|
||||
continue;
|
||||
}
|
||||
MeshInstance3D *mesh_3d = Object::cast_to<MeshInstance3D>(child);
|
||||
|
||||
if (!mesh_3d) {
|
||||
ERR_PRINT("AnimationTree: '" + String(E) + "', blend shape track does not point to MeshInstance3D: '" + String(path) + "'");
|
||||
continue;
|
||||
}
|
||||
|
||||
StringName blend_shape_name = path.get_subname(0);
|
||||
int blend_shape_idx = mesh_3d->find_blend_shape_by_name(blend_shape_name);
|
||||
if (blend_shape_idx == -1) {
|
||||
ERR_PRINT("AnimationTree: '" + String(E) + "', blend shape track points to a non-existing name: '" + String(blend_shape_name) + "'");
|
||||
continue;
|
||||
}
|
||||
|
||||
TrackCacheBlendShape *track_bshape = memnew(TrackCacheBlendShape);
|
||||
|
||||
track_bshape->mesh_3d = mesh_3d;
|
||||
track_bshape->shape_index = blend_shape_idx;
|
||||
|
||||
track_bshape->object = mesh_3d;
|
||||
track_bshape->object_id = mesh_3d->get_instance_id();
|
||||
track = track_bshape;
|
||||
#endif
|
||||
} break;
|
||||
case Animation::TYPE_METHOD: {
|
||||
TrackCacheMethod *track_method = memnew(TrackCacheMethod);
|
||||
|
@ -1087,6 +1118,28 @@ void AnimationTree::_process_graph(real_t p_delta) {
|
|||
|
||||
t->scale = t->scale.lerp(scale, blend);
|
||||
}
|
||||
#endif // _3D_DISABLED
|
||||
} break;
|
||||
case Animation::TYPE_BLEND_SHAPE: {
|
||||
#ifndef _3D_DISABLED
|
||||
TrackCacheBlendShape *t = static_cast<TrackCacheBlendShape *>(track);
|
||||
|
||||
if (t->process_pass != process_pass) {
|
||||
t->process_pass = process_pass;
|
||||
t->value = 0;
|
||||
}
|
||||
|
||||
float value;
|
||||
|
||||
Error err = a->blend_shape_track_interpolate(i, time, &value);
|
||||
//ERR_CONTINUE(err!=OK); //used for testing, should be removed
|
||||
|
||||
if (err != OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
t->value = Math::lerp(t->value, value, blend);
|
||||
|
||||
#endif // _3D_DISABLED
|
||||
} break;
|
||||
case Animation::TYPE_VALUE: {
|
||||
|
@ -1382,6 +1435,15 @@ void AnimationTree::_process_graph(real_t p_delta) {
|
|||
t->node_3d->set_scale(t->scale);
|
||||
}
|
||||
}
|
||||
#endif // _3D_DISABLED
|
||||
} break;
|
||||
case Animation::TYPE_BLEND_SHAPE: {
|
||||
#ifndef _3D_DISABLED
|
||||
TrackCacheBlendShape *t = static_cast<TrackCacheBlendShape *>(track);
|
||||
|
||||
if (t->mesh_3d) {
|
||||
t->mesh_3d->set_blend_shape_value(t->shape_index, t->value);
|
||||
}
|
||||
#endif // _3D_DISABLED
|
||||
} break;
|
||||
case Animation::TYPE_VALUE: {
|
||||
|
|
|
@ -210,6 +210,13 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
struct TrackCacheBlendShape : public TrackCache {
|
||||
MeshInstance3D *mesh_3d = nullptr;
|
||||
float value = 0;
|
||||
int shape_index = -1;
|
||||
TrackCacheBlendShape() { type = Animation::TYPE_BLEND_SHAPE; }
|
||||
};
|
||||
|
||||
struct TrackCacheValue : public TrackCache {
|
||||
Variant value;
|
||||
Vector<StringName> subpath;
|
||||
|
|
|
@ -49,6 +49,8 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
|
|||
add_track(TYPE_ROTATION_3D);
|
||||
} else if (type == "scale_3d") {
|
||||
add_track(TYPE_SCALE_3D);
|
||||
} else if (type == "blend_shape") {
|
||||
add_track(TYPE_BLEND_SHAPE);
|
||||
} else if (type == "value") {
|
||||
add_track(TYPE_VALUE);
|
||||
} else if (type == "method") {
|
||||
|
@ -146,6 +148,25 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
|
|||
sk.value.y = ofs[3];
|
||||
sk.value.z = ofs[4];
|
||||
}
|
||||
} else if (track_get_type(track) == TYPE_BLEND_SHAPE) {
|
||||
BlendShapeTrack *st = static_cast<BlendShapeTrack *>(tracks[track]);
|
||||
Vector<real_t> values = p_value;
|
||||
int vcount = values.size();
|
||||
ERR_FAIL_COND_V(vcount % BLEND_SHAPE_TRACK_SIZE, false);
|
||||
|
||||
const real_t *r = values.ptr();
|
||||
|
||||
int64_t count = vcount / BLEND_SHAPE_TRACK_SIZE;
|
||||
st->blend_shapes.resize(count);
|
||||
|
||||
TKey<float> *sw = st->blend_shapes.ptrw();
|
||||
for (int i = 0; i < count; i++) {
|
||||
TKey<float> &sk = sw[i];
|
||||
const real_t *ofs = &r[i * BLEND_SHAPE_TRACK_SIZE];
|
||||
sk.time = ofs[0];
|
||||
sk.transition = ofs[1];
|
||||
sk.value = ofs[2];
|
||||
}
|
||||
|
||||
} else if (track_get_type(track) == TYPE_VALUE) {
|
||||
ValueTrack *vt = static_cast<ValueTrack *>(tracks[track]);
|
||||
|
@ -369,6 +390,9 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
|
|||
case TYPE_SCALE_3D:
|
||||
r_ret = "scale_3d";
|
||||
break;
|
||||
case TYPE_BLEND_SHAPE:
|
||||
r_ret = "blend_shape";
|
||||
break;
|
||||
case TYPE_VALUE:
|
||||
r_ret = "value";
|
||||
break;
|
||||
|
@ -462,6 +486,25 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
|
|||
w[idx++] = scale.z;
|
||||
}
|
||||
|
||||
r_ret = keys;
|
||||
return true;
|
||||
} else if (track_get_type(track) == TYPE_BLEND_SHAPE) {
|
||||
Vector<real_t> keys;
|
||||
int kk = track_get_key_count(track);
|
||||
keys.resize(kk * BLEND_SHAPE_TRACK_SIZE);
|
||||
|
||||
real_t *w = keys.ptrw();
|
||||
|
||||
int idx = 0;
|
||||
for (int i = 0; i < track_get_key_count(track); i++) {
|
||||
float bs;
|
||||
blend_shape_track_get_key(track, i, &bs);
|
||||
|
||||
w[idx++] = track_get_key_time(track, i);
|
||||
w[idx++] = track_get_key_transition(track, i);
|
||||
w[idx++] = bs;
|
||||
}
|
||||
|
||||
r_ret = keys;
|
||||
return true;
|
||||
} else if (track_get_type(track) == TYPE_VALUE) {
|
||||
|
@ -682,6 +725,10 @@ int Animation::add_track(TrackType p_type, int p_at_pos) {
|
|||
ScaleTrack *st = memnew(ScaleTrack);
|
||||
tracks.insert(p_at_pos, st);
|
||||
} break;
|
||||
case TYPE_BLEND_SHAPE: {
|
||||
BlendShapeTrack *bst = memnew(BlendShapeTrack);
|
||||
tracks.insert(p_at_pos, bst);
|
||||
} break;
|
||||
case TYPE_VALUE: {
|
||||
tracks.insert(p_at_pos, memnew(ValueTrack));
|
||||
|
||||
|
@ -730,6 +777,11 @@ void Animation::remove_track(int p_track) {
|
|||
ScaleTrack *st = static_cast<ScaleTrack *>(t);
|
||||
_clear(st->scales);
|
||||
|
||||
} break;
|
||||
case TYPE_BLEND_SHAPE: {
|
||||
BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
|
||||
_clear(bst->blend_shapes);
|
||||
|
||||
} break;
|
||||
case TYPE_VALUE: {
|
||||
ValueTrack *vt = static_cast<ValueTrack *>(t);
|
||||
|
@ -993,6 +1045,53 @@ Error Animation::scale_track_interpolate(int p_track, double p_time, Vector3 *r_
|
|||
return OK;
|
||||
}
|
||||
|
||||
int Animation::blend_shape_track_insert_key(int p_track, double p_time, float p_blend_shape) {
|
||||
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
|
||||
Track *t = tracks[p_track];
|
||||
ERR_FAIL_COND_V(t->type != TYPE_BLEND_SHAPE, -1);
|
||||
|
||||
BlendShapeTrack *st = static_cast<BlendShapeTrack *>(t);
|
||||
|
||||
TKey<float> tkey;
|
||||
tkey.time = p_time;
|
||||
tkey.value = p_blend_shape;
|
||||
|
||||
int ret = _insert(p_time, st->blend_shapes, tkey);
|
||||
emit_changed();
|
||||
return ret;
|
||||
}
|
||||
|
||||
Error Animation::blend_shape_track_get_key(int p_track, int p_key, float *r_blend_shape) const {
|
||||
ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
|
||||
Track *t = tracks[p_track];
|
||||
|
||||
BlendShapeTrack *st = static_cast<BlendShapeTrack *>(t);
|
||||
ERR_FAIL_COND_V(t->type != TYPE_BLEND_SHAPE, ERR_INVALID_PARAMETER);
|
||||
ERR_FAIL_INDEX_V(p_key, st->blend_shapes.size(), ERR_INVALID_PARAMETER);
|
||||
|
||||
*r_blend_shape = st->blend_shapes[p_key].value;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error Animation::blend_shape_track_interpolate(int p_track, double p_time, float *r_interpolation) const {
|
||||
ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
|
||||
Track *t = tracks[p_track];
|
||||
ERR_FAIL_COND_V(t->type != TYPE_BLEND_SHAPE, ERR_INVALID_PARAMETER);
|
||||
|
||||
BlendShapeTrack *st = static_cast<BlendShapeTrack *>(t);
|
||||
|
||||
bool ok = false;
|
||||
|
||||
float tk = _interpolate(st->blend_shapes, p_time, st->interpolation, st->loop_wrap, &ok);
|
||||
|
||||
if (!ok) {
|
||||
return ERR_UNAVAILABLE;
|
||||
}
|
||||
*r_interpolation = tk;
|
||||
return OK;
|
||||
}
|
||||
|
||||
void Animation::track_remove_key_at_time(int p_track, double p_time) {
|
||||
int idx = track_find_key(p_track, p_time, true);
|
||||
ERR_FAIL_COND(idx < 0);
|
||||
|
@ -1021,6 +1120,12 @@ void Animation::track_remove_key(int p_track, int p_idx) {
|
|||
ERR_FAIL_INDEX(p_idx, st->scales.size());
|
||||
st->scales.remove(p_idx);
|
||||
|
||||
} break;
|
||||
case TYPE_BLEND_SHAPE: {
|
||||
BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
|
||||
ERR_FAIL_INDEX(p_idx, bst->blend_shapes.size());
|
||||
bst->blend_shapes.remove(p_idx);
|
||||
|
||||
} break;
|
||||
case TYPE_VALUE: {
|
||||
ValueTrack *vt = static_cast<ValueTrack *>(t);
|
||||
|
@ -1087,12 +1192,24 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
|
|||
|
||||
} break;
|
||||
case TYPE_SCALE_3D: {
|
||||
ScaleTrack *rt = static_cast<ScaleTrack *>(t);
|
||||
int k = _find(rt->scales, p_time);
|
||||
if (k < 0 || k >= rt->scales.size()) {
|
||||
ScaleTrack *st = static_cast<ScaleTrack *>(t);
|
||||
int k = _find(st->scales, p_time);
|
||||
if (k < 0 || k >= st->scales.size()) {
|
||||
return -1;
|
||||
}
|
||||
if (rt->scales[k].time != p_time && p_exact) {
|
||||
if (st->scales[k].time != p_time && p_exact) {
|
||||
return -1;
|
||||
}
|
||||
return k;
|
||||
|
||||
} break;
|
||||
case TYPE_BLEND_SHAPE: {
|
||||
BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
|
||||
int k = _find(bst->blend_shapes, p_time);
|
||||
if (k < 0 || k >= bst->blend_shapes.size()) {
|
||||
return -1;
|
||||
}
|
||||
if (bst->blend_shapes[k].time != p_time && p_exact) {
|
||||
return -1;
|
||||
}
|
||||
return k;
|
||||
|
@ -1185,6 +1302,12 @@ void Animation::track_insert_key(int p_track, double p_time, const Variant &p_ke
|
|||
int idx = scale_track_insert_key(p_track, p_time, p_key);
|
||||
track_set_key_transition(p_track, idx, p_transition);
|
||||
|
||||
} break;
|
||||
case TYPE_BLEND_SHAPE: {
|
||||
ERR_FAIL_COND((p_key.get_type() != Variant::FLOAT) && (p_key.get_type() != Variant::INT));
|
||||
int idx = blend_shape_track_insert_key(p_track, p_time, p_key);
|
||||
track_set_key_transition(p_track, idx, p_transition);
|
||||
|
||||
} break;
|
||||
case TYPE_VALUE: {
|
||||
ValueTrack *vt = static_cast<ValueTrack *>(t);
|
||||
|
@ -1279,6 +1402,10 @@ int Animation::track_get_key_count(int p_track) const {
|
|||
ScaleTrack *st = static_cast<ScaleTrack *>(t);
|
||||
return st->scales.size();
|
||||
} break;
|
||||
case TYPE_BLEND_SHAPE: {
|
||||
BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
|
||||
return bst->blend_shapes.size();
|
||||
} break;
|
||||
case TYPE_VALUE: {
|
||||
ValueTrack *vt = static_cast<ValueTrack *>(t);
|
||||
return vt->values.size();
|
||||
|
@ -1328,6 +1455,12 @@ Variant Animation::track_get_key_value(int p_track, int p_key_idx) const {
|
|||
|
||||
return st->scales[p_key_idx].value;
|
||||
} break;
|
||||
case TYPE_BLEND_SHAPE: {
|
||||
BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
|
||||
ERR_FAIL_INDEX_V(p_key_idx, bst->blend_shapes.size(), Variant());
|
||||
|
||||
return bst->blend_shapes[p_key_idx].value;
|
||||
} break;
|
||||
case TYPE_VALUE: {
|
||||
ValueTrack *vt = static_cast<ValueTrack *>(t);
|
||||
ERR_FAIL_INDEX_V(p_key_idx, vt->values.size(), Variant());
|
||||
|
@ -1400,6 +1533,11 @@ double Animation::track_get_key_time(int p_track, int p_key_idx) const {
|
|||
ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), -1);
|
||||
return st->scales[p_key_idx].time;
|
||||
} break;
|
||||
case TYPE_BLEND_SHAPE: {
|
||||
BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
|
||||
ERR_FAIL_INDEX_V(p_key_idx, bst->blend_shapes.size(), -1);
|
||||
return bst->blend_shapes[p_key_idx].time;
|
||||
} break;
|
||||
case TYPE_VALUE: {
|
||||
ValueTrack *vt = static_cast<ValueTrack *>(t);
|
||||
ERR_FAIL_INDEX_V(p_key_idx, vt->values.size(), -1);
|
||||
|
@ -1467,6 +1605,15 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) {
|
|||
_insert(p_time, tt->scales, key);
|
||||
return;
|
||||
}
|
||||
case TYPE_BLEND_SHAPE: {
|
||||
BlendShapeTrack *tt = static_cast<BlendShapeTrack *>(t);
|
||||
ERR_FAIL_INDEX(p_key_idx, tt->blend_shapes.size());
|
||||
TKey<float> key = tt->blend_shapes[p_key_idx];
|
||||
key.time = p_time;
|
||||
tt->blend_shapes.remove(p_key_idx);
|
||||
_insert(p_time, tt->blend_shapes, key);
|
||||
return;
|
||||
}
|
||||
case TYPE_VALUE: {
|
||||
ValueTrack *vt = static_cast<ValueTrack *>(t);
|
||||
ERR_FAIL_INDEX(p_key_idx, vt->values.size());
|
||||
|
@ -1537,6 +1684,11 @@ real_t Animation::track_get_key_transition(int p_track, int p_key_idx) const {
|
|||
ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), -1);
|
||||
return st->scales[p_key_idx].transition;
|
||||
} break;
|
||||
case TYPE_BLEND_SHAPE: {
|
||||
BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
|
||||
ERR_FAIL_INDEX_V(p_key_idx, bst->blend_shapes.size(), -1);
|
||||
return bst->blend_shapes[p_key_idx].transition;
|
||||
} break;
|
||||
case TYPE_VALUE: {
|
||||
ValueTrack *vt = static_cast<ValueTrack *>(t);
|
||||
ERR_FAIL_INDEX_V(p_key_idx, vt->values.size(), -1);
|
||||
|
@ -1591,6 +1743,14 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p
|
|||
|
||||
st->scales.write[p_key_idx].value = p_value;
|
||||
|
||||
} break;
|
||||
case TYPE_BLEND_SHAPE: {
|
||||
ERR_FAIL_COND((p_value.get_type() != Variant::FLOAT) && (p_value.get_type() != Variant::INT));
|
||||
BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
|
||||
ERR_FAIL_INDEX(p_key_idx, bst->blend_shapes.size());
|
||||
|
||||
bst->blend_shapes.write[p_key_idx].value = p_value;
|
||||
|
||||
} break;
|
||||
case TYPE_VALUE: {
|
||||
ValueTrack *vt = static_cast<ValueTrack *>(t);
|
||||
|
@ -1673,6 +1833,11 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, real_t p_tr
|
|||
ERR_FAIL_INDEX(p_key_idx, st->scales.size());
|
||||
st->scales.write[p_key_idx].transition = p_transition;
|
||||
} break;
|
||||
case TYPE_BLEND_SHAPE: {
|
||||
BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
|
||||
ERR_FAIL_INDEX(p_key_idx, bst->blend_shapes.size());
|
||||
bst->blend_shapes.write[p_key_idx].transition = p_transition;
|
||||
} break;
|
||||
case TYPE_VALUE: {
|
||||
ValueTrack *vt = static_cast<ValueTrack *>(t);
|
||||
ERR_FAIL_INDEX(p_key_idx, vt->values.size());
|
||||
|
@ -2184,6 +2349,12 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
|
|||
_track_get_key_indices_in_range(st->scales, from_time, length, p_indices);
|
||||
_track_get_key_indices_in_range(st->scales, 0, to_time, p_indices);
|
||||
|
||||
} break;
|
||||
case TYPE_BLEND_SHAPE: {
|
||||
const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
|
||||
_track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices);
|
||||
_track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices);
|
||||
|
||||
} break;
|
||||
case TYPE_VALUE: {
|
||||
const ValueTrack *vt = static_cast<const ValueTrack *>(t);
|
||||
|
@ -2249,6 +2420,11 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
|
|||
const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
|
||||
_track_get_key_indices_in_range(st->scales, from_time, to_time, p_indices);
|
||||
|
||||
} break;
|
||||
case TYPE_BLEND_SHAPE: {
|
||||
const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
|
||||
_track_get_key_indices_in_range(bst->blend_shapes, from_time, to_time, p_indices);
|
||||
|
||||
} break;
|
||||
case TYPE_VALUE: {
|
||||
const ValueTrack *vt = static_cast<const ValueTrack *>(t);
|
||||
|
@ -2867,6 +3043,7 @@ void Animation::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("position_track_insert_key", "track_idx", "time", "position"), &Animation::position_track_insert_key);
|
||||
ClassDB::bind_method(D_METHOD("rotation_track_insert_key", "track_idx", "time", "rotation"), &Animation::rotation_track_insert_key);
|
||||
ClassDB::bind_method(D_METHOD("scale_track_insert_key", "track_idx", "time", "scale"), &Animation::scale_track_insert_key);
|
||||
ClassDB::bind_method(D_METHOD("blend_shape_track_insert_key", "track_idx", "time", "amount"), &Animation::blend_shape_track_insert_key);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("track_insert_key", "track_idx", "time", "key", "transition"), &Animation::track_insert_key, DEFVAL(1));
|
||||
ClassDB::bind_method(D_METHOD("track_remove_key", "track_idx", "key_idx"), &Animation::track_remove_key);
|
||||
|
@ -2943,6 +3120,7 @@ void Animation::_bind_methods() {
|
|||
BIND_ENUM_CONSTANT(TYPE_POSITION_3D);
|
||||
BIND_ENUM_CONSTANT(TYPE_ROTATION_3D);
|
||||
BIND_ENUM_CONSTANT(TYPE_SCALE_3D);
|
||||
BIND_ENUM_CONSTANT(TYPE_BLEND_SHAPE);
|
||||
BIND_ENUM_CONSTANT(TYPE_METHOD);
|
||||
BIND_ENUM_CONSTANT(TYPE_BEZIER);
|
||||
BIND_ENUM_CONSTANT(TYPE_AUDIO);
|
||||
|
@ -3089,6 +3267,41 @@ bool Animation::_scale_track_optimize_key(const TKey<Vector3> &t0, const TKey<Ve
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Animation::_blend_shape_track_optimize_key(const TKey<float> &t0, const TKey<float> &t1, const TKey<float> &t2, real_t p_allowed_unit_error) {
|
||||
float v0 = t0.value;
|
||||
float v1 = t1.value;
|
||||
float v2 = t2.value;
|
||||
|
||||
if (Math::is_equal_approx(v1, v2, p_allowed_unit_error)) {
|
||||
//0 and 2 are close, let's see if 1 is close
|
||||
if (!Math::is_equal_approx(v0, v1, p_allowed_unit_error)) {
|
||||
//not close, not optimizable
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
/*
|
||||
TODO eventually discuss a way to optimize these better.
|
||||
float pd = (v2 - v0);
|
||||
real_t d0 = pd.dot(v0);
|
||||
real_t d1 = pd.dot(v1);
|
||||
real_t d2 = pd.dot(v2);
|
||||
if (d1 < d0 || d1 > d2) {
|
||||
return false; //beyond segment range
|
||||
}
|
||||
|
||||
float s[2] = { v0, v2 };
|
||||
real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1);
|
||||
|
||||
if (d > pd.length() * p_allowed_linear_error) {
|
||||
return false; //beyond allowed error for colinearity
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Animation::_position_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err) {
|
||||
ERR_FAIL_INDEX(p_idx, tracks.size());
|
||||
ERR_FAIL_COND(tracks[p_idx]->type != TYPE_POSITION_3D);
|
||||
|
@ -3197,6 +3410,41 @@ void Animation::_scale_track_optimize(int p_idx, real_t p_allowed_linear_err) {
|
|||
}
|
||||
}
|
||||
|
||||
void Animation::_blend_shape_track_optimize(int p_idx, real_t p_allowed_linear_err) {
|
||||
ERR_FAIL_INDEX(p_idx, tracks.size());
|
||||
ERR_FAIL_COND(tracks[p_idx]->type != TYPE_BLEND_SHAPE);
|
||||
BlendShapeTrack *tt = static_cast<BlendShapeTrack *>(tracks[p_idx]);
|
||||
bool prev_erased = false;
|
||||
TKey<float> first_erased;
|
||||
first_erased.value = 0.0;
|
||||
|
||||
for (int i = 1; i < tt->blend_shapes.size() - 1; i++) {
|
||||
TKey<float> &t0 = tt->blend_shapes.write[i - 1];
|
||||
TKey<float> &t1 = tt->blend_shapes.write[i];
|
||||
TKey<float> &t2 = tt->blend_shapes.write[i + 1];
|
||||
|
||||
bool erase = _blend_shape_track_optimize_key(t0, t1, t2, p_allowed_linear_err);
|
||||
|
||||
if (prev_erased && !_blend_shape_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err)) {
|
||||
//avoid error to go beyond first erased key
|
||||
erase = false;
|
||||
}
|
||||
|
||||
if (erase) {
|
||||
if (!prev_erased) {
|
||||
first_erased = t1;
|
||||
prev_erased = true;
|
||||
}
|
||||
|
||||
tt->blend_shapes.remove(i);
|
||||
i--;
|
||||
|
||||
} else {
|
||||
prev_erased = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Animation::optimize(real_t p_allowed_linear_err, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) {
|
||||
for (int i = 0; i < tracks.size(); i++) {
|
||||
if (tracks[i]->type == TYPE_POSITION_3D) {
|
||||
|
@ -3205,6 +3453,8 @@ void Animation::optimize(real_t p_allowed_linear_err, real_t p_allowed_angular_e
|
|||
_rotation_track_optimize(i, p_allowed_angular_err, p_max_optimizable_angle);
|
||||
} else if (tracks[i]->type == TYPE_SCALE_3D) {
|
||||
_scale_track_optimize(i, p_allowed_linear_err);
|
||||
} else if (tracks[i]->type == TYPE_BLEND_SHAPE) {
|
||||
_blend_shape_track_optimize(i, p_allowed_linear_err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ public:
|
|||
TYPE_POSITION_3D, ///< Position 3D track
|
||||
TYPE_ROTATION_3D, ///< Rotation 3D track
|
||||
TYPE_SCALE_3D, ///< Scale 3D track
|
||||
TYPE_BLEND_SHAPE, ///< Blend Shape track
|
||||
TYPE_METHOD, ///< Call any method on a specific node.
|
||||
TYPE_BEZIER, ///< Bezier curve
|
||||
TYPE_AUDIO,
|
||||
|
@ -91,6 +92,7 @@ private:
|
|||
const int32_t POSITION_TRACK_SIZE = 5;
|
||||
const int32_t ROTATION_TRACK_SIZE = 6;
|
||||
const int32_t SCALE_TRACK_SIZE = 5;
|
||||
const int32_t BLEND_SHAPE_TRACK_SIZE = 3;
|
||||
|
||||
/* POSITION TRACK */
|
||||
|
||||
|
@ -115,6 +117,13 @@ private:
|
|||
ScaleTrack() { type = TYPE_SCALE_3D; }
|
||||
};
|
||||
|
||||
/* BLEND SHAPE TRACK */
|
||||
|
||||
struct BlendShapeTrack : public Track {
|
||||
Vector<TKey<float>> blend_shapes;
|
||||
BlendShapeTrack() { type = TYPE_BLEND_SHAPE; }
|
||||
};
|
||||
|
||||
/* PROPERTY VALUE TRACK */
|
||||
|
||||
struct ValueTrack : public Track {
|
||||
|
@ -247,10 +256,12 @@ private:
|
|||
bool _position_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_alowed_linear_err, real_t p_allowed_angular_error, const Vector3 &p_norm);
|
||||
bool _rotation_track_optimize_key(const TKey<Quaternion> &t0, const TKey<Quaternion> &t1, const TKey<Quaternion> &t2, real_t p_allowed_angular_error, float p_max_optimizable_angle);
|
||||
bool _scale_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_error);
|
||||
bool _blend_shape_track_optimize_key(const TKey<float> &t0, const TKey<float> &t1, const TKey<float> &t2, real_t p_allowed_unit_error);
|
||||
|
||||
void _position_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err);
|
||||
void _rotation_track_optimize(int p_idx, real_t p_allowed_angular_err, real_t p_max_optimizable_angle);
|
||||
void _scale_track_optimize(int p_idx, real_t p_allowed_linear_err);
|
||||
void _blend_shape_track_optimize(int p_idx, real_t p_allowed_unit_error);
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
|
@ -308,6 +319,10 @@ public:
|
|||
Error scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) const;
|
||||
Error scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const;
|
||||
|
||||
int blend_shape_track_insert_key(int p_track, double p_time, float p_blend);
|
||||
Error blend_shape_track_get_key(int p_track, int p_key, float *r_blend) const;
|
||||
Error blend_shape_track_interpolate(int p_track, double p_time, float *r_blend) const;
|
||||
|
||||
void track_set_interpolation_type(int p_track, InterpolationType p_interp);
|
||||
InterpolationType track_get_interpolation_type(int p_track) const;
|
||||
|
||||
|
|
Loading…
Reference in New Issue