summaryrefslogtreecommitdiffstats
path: root/scene/resources/animation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/resources/animation.cpp')
-rw-r--r--scene/resources/animation.cpp161
1 files changed, 156 insertions, 5 deletions
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index a2ed6af23c..57a4e35f7a 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -63,6 +63,23 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
}
compression.enabled = true;
return true;
+ } else if (prop_name == SNAME("markers")) {
+ Array markers = p_value;
+ for (const Dictionary marker : markers) {
+ ERR_FAIL_COND_V(!marker.has("name"), false);
+ ERR_FAIL_COND_V(!marker.has("time"), false);
+ StringName marker_name = marker["name"];
+ double time = marker["time"];
+ _marker_insert(time, marker_names, MarkerKey(time, marker_name));
+ marker_times.insert(marker_name, time);
+ Color color = Color(1, 1, 1);
+ if (marker.has("color")) {
+ color = marker["color"];
+ }
+ marker_colors.insert(marker_name, color);
+ }
+
+ return true;
} else if (prop_name.begins_with("tracks/")) {
int track = prop_name.get_slicec('/', 1).to_int();
String what = prop_name.get_slicec('/', 2);
@@ -321,8 +338,12 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
Vector<real_t> times = d["times"];
Vector<real_t> values = d["points"];
#ifdef TOOLS_ENABLED
- ERR_FAIL_COND_V(!d.has("handle_modes"), false);
- Vector<int> handle_modes = d["handle_modes"];
+ Vector<int> handle_modes;
+ if (d.has("handle_modes")) {
+ handle_modes = d["handle_modes"];
+ } else {
+ handle_modes.resize_zeroed(times.size());
+ }
#endif // TOOLS_ENABLED
ERR_FAIL_COND_V(times.size() * 5 != values.size(), false);
@@ -466,6 +487,18 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = comp;
return true;
+ } else if (prop_name == SNAME("markers")) {
+ Array markers;
+
+ for (HashMap<StringName, double>::ConstIterator E = marker_times.begin(); E; ++E) {
+ Dictionary d;
+ d["name"] = E->key;
+ d["time"] = E->value;
+ d["color"] = marker_colors[E->key];
+ markers.push_back(d);
+ }
+
+ r_ret = markers;
} else if (prop_name == "length") {
r_ret = length;
} else if (prop_name == "loop_mode") {
@@ -835,6 +868,7 @@ void Animation::_get_property_list(List<PropertyInfo> *p_list) const {
if (compression.enabled) {
p_list->push_back(PropertyInfo(Variant::DICTIONARY, "_compression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "markers", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
for (int i = 0; i < tracks.size(); i++) {
p_list->push_back(PropertyInfo(Variant::STRING, "tracks/" + itos(i) + "/type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/imported", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
@@ -1083,6 +1117,27 @@ int Animation::_insert(double p_time, T &p_keys, const V &p_value) {
return -1;
}
+int Animation::_marker_insert(double p_time, Vector<MarkerKey> &p_keys, const MarkerKey &p_value) {
+ int idx = p_keys.size();
+
+ while (true) {
+ // Condition for replacement.
+ if (idx > 0 && Math::is_equal_approx((double)p_keys[idx - 1].time, p_time)) {
+ p_keys.write[idx - 1] = p_value;
+ return idx - 1;
+
+ // Condition for insert.
+ } else if (idx == 0 || p_keys[idx - 1].time < p_time) {
+ p_keys.insert(idx, p_value);
+ return idx;
+ }
+
+ idx--;
+ }
+
+ return -1;
+}
+
template <typename T>
void Animation::_clear(T &p_keys) {
p_keys.clear();
@@ -3159,6 +3214,90 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
}
}
+void Animation::add_marker(const StringName &p_name, double p_time) {
+ int idx = _find(marker_names, p_time);
+
+ if (idx >= 0 && idx < marker_names.size() && Math::is_equal_approx(p_time, marker_names[idx].time)) {
+ marker_times.erase(marker_names[idx].name);
+ marker_colors.erase(marker_names[idx].name);
+ marker_names.write[idx].name = p_name;
+ marker_times.insert(p_name, p_time);
+ marker_colors.insert(p_name, Color(1, 1, 1));
+ } else {
+ _marker_insert(p_time, marker_names, MarkerKey(p_time, p_name));
+ marker_times.insert(p_name, p_time);
+ marker_colors.insert(p_name, Color(1, 1, 1));
+ }
+}
+
+void Animation::remove_marker(const StringName &p_name) {
+ HashMap<StringName, double>::Iterator E = marker_times.find(p_name);
+ ERR_FAIL_COND(!E);
+ int idx = _find(marker_names, E->value);
+ bool success = idx >= 0 && idx < marker_names.size() && Math::is_equal_approx(marker_names[idx].time, E->value);
+ ERR_FAIL_COND(!success);
+ marker_names.remove_at(idx);
+ marker_times.remove(E);
+ marker_colors.erase(p_name);
+}
+
+bool Animation::has_marker(const StringName &p_name) const {
+ return marker_times.has(p_name);
+}
+
+StringName Animation::get_marker_at_time(double p_time) const {
+ int idx = _find(marker_names, p_time);
+
+ if (idx >= 0 && idx < marker_names.size() && Math::is_equal_approx(marker_names[idx].time, p_time)) {
+ return marker_names[idx].name;
+ }
+
+ return StringName();
+}
+
+StringName Animation::get_next_marker(double p_time) const {
+ int idx = _find(marker_names, p_time);
+
+ if (idx >= -1 && idx < marker_names.size() - 1) {
+ // _find ensures that the time at idx is always the closest time to p_time that is also smaller to it.
+ // So we add 1 to get the next marker.
+ return marker_names[idx + 1].name;
+ }
+ return StringName();
+}
+
+StringName Animation::get_prev_marker(double p_time) const {
+ int idx = _find(marker_names, p_time);
+
+ if (idx >= 0 && idx < marker_names.size()) {
+ return marker_names[idx].name;
+ }
+ return StringName();
+}
+
+double Animation::get_marker_time(const StringName &p_name) const {
+ ERR_FAIL_COND_V(!marker_times.has(p_name), -1);
+ return marker_times.get(p_name);
+}
+
+PackedStringArray Animation::get_marker_names() const {
+ PackedStringArray names;
+ // We iterate on marker_names so the result is sorted by time.
+ for (const MarkerKey &marker_name : marker_names) {
+ names.push_back(marker_name.name);
+ }
+ return names;
+}
+
+Color Animation::get_marker_color(const StringName &p_name) const {
+ ERR_FAIL_COND_V(!marker_colors.has(p_name), Color());
+ return marker_colors[p_name];
+}
+
+void Animation::set_marker_color(const StringName &p_name, const Color &p_color) {
+ marker_colors[p_name] = p_color;
+}
+
Vector<Variant> Animation::method_track_get_params(int p_track, int p_key_idx) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), Vector<Variant>());
Track *t = tracks[p_track];
@@ -3890,6 +4029,17 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("animation_track_set_key_animation", "track_idx", "key_idx", "animation"), &Animation::animation_track_set_key_animation);
ClassDB::bind_method(D_METHOD("animation_track_get_key_animation", "track_idx", "key_idx"), &Animation::animation_track_get_key_animation);
+ ClassDB::bind_method(D_METHOD("add_marker", "name", "time"), &Animation::add_marker);
+ ClassDB::bind_method(D_METHOD("remove_marker", "name"), &Animation::remove_marker);
+ ClassDB::bind_method(D_METHOD("has_marker", "name"), &Animation::has_marker);
+ ClassDB::bind_method(D_METHOD("get_marker_at_time", "time"), &Animation::get_marker_at_time);
+ ClassDB::bind_method(D_METHOD("get_next_marker", "time"), &Animation::get_next_marker);
+ ClassDB::bind_method(D_METHOD("get_prev_marker", "time"), &Animation::get_prev_marker);
+ ClassDB::bind_method(D_METHOD("get_marker_time", "name"), &Animation::get_marker_time);
+ ClassDB::bind_method(D_METHOD("get_marker_names"), &Animation::get_marker_names);
+ ClassDB::bind_method(D_METHOD("get_marker_color", "name"), &Animation::get_marker_color);
+ ClassDB::bind_method(D_METHOD("set_marker_color", "name", "color"), &Animation::set_marker_color);
+
ClassDB::bind_method(D_METHOD("set_length", "time_sec"), &Animation::set_length);
ClassDB::bind_method(D_METHOD("get_length"), &Animation::get_length);
@@ -3902,6 +4052,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &Animation::clear);
ClassDB::bind_method(D_METHOD("copy_track", "track_idx", "to_animation"), &Animation::copy_track);
+ ClassDB::bind_method(D_METHOD("optimize", "allowed_velocity_err", "allowed_angular_err", "precision"), &Animation::optimize, DEFVAL(0.01), DEFVAL(0.01), DEFVAL(3));
ClassDB::bind_method(D_METHOD("compress", "page_size", "fps", "split_tolerance"), &Animation::compress, DEFVAL(8192), DEFVAL(120), DEFVAL(4.0));
ClassDB::bind_method(D_METHOD("is_capture_included"), &Animation::is_capture_included);
@@ -4804,9 +4955,9 @@ void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tol
continue; // This track is exhausted (all keys were added already), don't consider.
}
}
-
- uint32_t key_frame = double(track_get_key_time(uncomp_track, time_tracks[i].key_index)) / frame_len;
-
+ double key_time = track_get_key_time(uncomp_track, time_tracks[i].key_index);
+ double result = key_time / frame_len;
+ uint32_t key_frame = Math::fast_ftoi(result);
if (time_tracks[i].needs_start_frame && key_frame > base_page_frame) {
start_frame = true;
best_frame = base_page_frame;