summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/3d/mesh_instance_3d.cpp74
-rw-r--r--scene/3d/mesh_instance_3d.h15
-rw-r--r--scene/3d/node_3d.cpp4
-rw-r--r--scene/animation/animation_player.cpp52
-rw-r--r--scene/animation/animation_player.h11
-rw-r--r--scene/animation/animation_tree.cpp62
-rw-r--r--scene/animation/animation_tree.h7
-rw-r--r--scene/resources/animation.cpp258
-rw-r--r--scene/resources/animation.h15
9 files changed, 468 insertions, 30 deletions
diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp
index cfd90e5da0..c148f95461 100644
--- a/scene/3d/mesh_instance_3d.cpp
+++ b/scene/3d/mesh_instance_3d.cpp
@@ -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);
diff --git a/scene/3d/mesh_instance_3d.h b/scene/3d/mesh_instance_3d.h
index beb7f6cf95..8f21726601 100644
--- a/scene/3d/mesh_instance_3d.h
+++ b/scene/3d/mesh_instance_3d.h
@@ -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;
diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp
index 272a06bce4..c96204cf60 100644
--- a/scene/3d/node_3d.cpp
+++ b/scene/3d/node_3d.cpp
@@ -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;
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 8407e0dd99..1f000d5f39 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -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) {
@@ -481,6 +506,31 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
}
#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: {
if (!nc->node) {
continue;
@@ -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);
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index f8e72a67b6..21e309efe0 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -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;
}
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index dd5fe46223..ccb5fa9472 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -640,6 +640,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);
@@ -1089,6 +1120,28 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
#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: {
TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
@@ -1384,6 +1437,15 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
#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: {
TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index ed207dfe0c..21b49937a7 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -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;
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index 6612c5ff3c..08b78a39b1 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -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;
@@ -464,6 +488,25 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
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) {
const ValueTrack *vt = static_cast<const ValueTrack *>(tracks[track]);
@@ -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));
@@ -731,6 +778,11 @@ void Animation::remove_track(int p_track) {
_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);
_clear(vt->values);
@@ -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);
@@ -1022,6 +1121,12 @@ void Animation::track_remove_key(int p_track, int p_idx) {
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);
ERR_FAIL_INDEX(p_idx, vt->values.size());
@@ -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 (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 (rt->scales[k].time != p_time && p_exact) {
+ if (bst->blend_shapes[k].time != p_time && p_exact) {
return -1;
}
return k;
@@ -1186,6 +1303,12 @@ void Animation::track_insert_key(int p_track, double p_time, const Variant &p_ke
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);
@@ -1592,6 +1744,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);
ERR_FAIL_INDEX(p_key_idx, vt->values.size());
@@ -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());
@@ -2185,6 +2350,12 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
_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);
_track_get_key_indices_in_range(vt->values, from_time, length, p_indices);
@@ -2250,6 +2421,11 @@ 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, 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);
_track_get_key_indices_in_range(vt->values, from_time, to_time, p_indices);
@@ -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);
}
}
}
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 614e12a560..4ee0741d87 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -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;