summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHugo Locurcio <hugo.locurcio@hugo.pro>2022-06-26 01:38:20 +0200
committerHugo Locurcio <hugo.locurcio@hugo.pro>2022-07-27 18:50:28 +0200
commitaaeb60eafc44cda6151a1c6f06ac709335f06f00 (patch)
treeefb119dd03a6b5cfd245f423ff36ee5f82bdcdec
parent03987738aae247d9a62c239a3cdf2546612ca84a (diff)
Add a Movie Quit On Finish property to AnimationPlayer
This quits the project when an animation is done playing in the given AnimationPlayer, but only in Movie Maker mode. When this happens, a message is printed with the absolute path of the AnimationPlayer node that caused the engine to quit. This can be used to create videos that stop at a specified time without having to write any script. A report is now also printed to the console when the video is done recording (as long as the engine was exited properly). This report is unfortunately not always visible in the editor's Output panel, as it's printed too late. A method was also added to get the path to the output file from the scripting API.
-rw-r--r--core/config/engine.cpp8
-rw-r--r--core/config/engine.h4
-rw-r--r--core/core_bind.cpp6
-rw-r--r--core/core_bind.h3
-rw-r--r--doc/classes/AnimationPlayer.xml5
-rw-r--r--doc/classes/Engine.xml6
-rw-r--r--main/main.cpp17
-rw-r--r--scene/animation/animation_player.cpp19
-rw-r--r--scene/animation/animation_player.h4
-rw-r--r--servers/movie_writer/movie_writer.cpp26
10 files changed, 88 insertions, 10 deletions
diff --git a/core/config/engine.cpp b/core/config/engine.cpp
index 782d369ae6..44ad4961d9 100644
--- a/core/config/engine.cpp
+++ b/core/config/engine.cpp
@@ -246,6 +246,14 @@ void Engine::get_singletons(List<Singleton> *p_singletons) {
}
}
+String Engine::get_write_movie_path() const {
+ return write_movie_path;
+}
+
+void Engine::set_write_movie_path(const String &p_path) {
+ write_movie_path = p_path;
+}
+
void Engine::set_shader_cache_path(const String &p_path) {
shader_cache_path = p_path;
}
diff --git a/core/config/engine.h b/core/config/engine.h
index 82e3ee5487..68562643e7 100644
--- a/core/config/engine.h
+++ b/core/config/engine.h
@@ -76,6 +76,7 @@ private:
static Engine *singleton;
+ String write_movie_path;
String shader_cache_path;
public:
@@ -138,6 +139,9 @@ public:
Dictionary get_license_info() const;
String get_license_text() const;
+ void set_write_movie_path(const String &p_path);
+ String get_write_movie_path() const;
+
void set_shader_cache_path(const String &p_path);
String get_shader_cache_path() const;
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 8e776d6f05..e5d5464643 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -2290,6 +2290,10 @@ bool Engine::is_editor_hint() const {
return ::Engine::get_singleton()->is_editor_hint();
}
+String Engine::get_write_movie_path() const {
+ return ::Engine::get_singleton()->get_write_movie_path();
+}
+
void Engine::set_print_error_messages(bool p_enabled) {
::Engine::get_singleton()->set_print_error_messages(p_enabled);
}
@@ -2339,6 +2343,8 @@ void Engine::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_editor_hint"), &Engine::is_editor_hint);
+ ClassDB::bind_method(D_METHOD("get_write_movie_path"), &Engine::get_write_movie_path);
+
ClassDB::bind_method(D_METHOD("set_print_error_messages", "enabled"), &Engine::set_print_error_messages);
ClassDB::bind_method(D_METHOD("is_printing_error_messages"), &Engine::is_printing_error_messages);
diff --git a/core/core_bind.h b/core/core_bind.h
index e222a220f0..f98de81354 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -676,6 +676,9 @@ public:
void set_editor_hint(bool p_enabled);
bool is_editor_hint() const;
+ // `set_write_movie_path()` is not exposed to the scripting API as changing it at run-time has no effect.
+ String get_write_movie_path() const;
+
void set_print_error_messages(bool p_enabled);
bool is_printing_error_messages() const;
diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml
index 653607610d..b24c439432 100644
--- a/doc/classes/AnimationPlayer.xml
+++ b/doc/classes/AnimationPlayer.xml
@@ -220,6 +220,10 @@
<member name="method_call_mode" type="int" setter="set_method_call_mode" getter="get_method_call_mode" enum="AnimationPlayer.AnimationMethodCallMode" default="0">
The call mode to use for Call Method tracks.
</member>
+ <member name="movie_quit_on_finish" type="bool" setter="set_movie_quit_on_finish_enabled" getter="is_movie_quit_on_finish_enabled" default="false">
+ If [code]true[/code] and the engine is running in Movie Maker mode (see [MovieWriter]), exits the engine with [method SceneTree.quit] as soon as an animation is done playing in this [AnimationPlayer]. A message is printed when the engine quits for this reason.
+ [b]Note:[/b] This obeys the same logic as the [signal animation_finished] signal, so it will not quit the engine if the animation is set to be looping.
+ </member>
<member name="playback_active" type="bool" setter="set_active" getter="is_active">
If [code]true[/code], updates animations in response to process-related notifications.
</member>
@@ -253,6 +257,7 @@
<argument index="0" name="anim_name" type="StringName" />
<description>
Notifies when an animation finished playing.
+ [b]Note:[/b] This signal is not emitted if an animation is looping.
</description>
</signal>
<signal name="animation_started">
diff --git a/doc/classes/Engine.xml b/doc/classes/Engine.xml
index 506992e3af..36dfee833b 100644
--- a/doc/classes/Engine.xml
+++ b/doc/classes/Engine.xml
@@ -151,6 +151,12 @@
[/codeblocks]
</description>
</method>
+ <method name="get_write_movie_path" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns the path to the [MovieWriter]'s output file, or an empty string if the engine wasn't started in Movie Maker mode. This path can be absolute or relative depending on how the user specified it.
+ </description>
+ </method>
<method name="has_singleton" qualifiers="const">
<return type="bool" />
<argument index="0" name="name" type="StringName" />
diff --git a/main/main.cpp b/main/main.cpp
index 73950f3471..ab6c0fbc6a 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -181,7 +181,6 @@ static bool debug_navigation = false;
static int frame_delay = 0;
static bool disable_render_loop = false;
static int fixed_fps = -1;
-static String write_movie_path;
static MovieWriter *movie_writer = nullptr;
static bool disable_vsync = false;
static bool print_fps = false;
@@ -1162,7 +1161,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
}
} else if (I->get() == "--write-movie") {
if (I->next()) {
- write_movie_path = I->next()->get();
+ Engine::get_singleton()->set_write_movie_path(I->next()->get());
N = I->next()->next();
if (fixed_fps == -1) {
fixed_fps = 60;
@@ -1512,7 +1511,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
audio_driver_idx = 0;
}
- if (write_movie_path != String()) {
+ if (Engine::get_singleton()->get_write_movie_path() != String()) {
// Always use dummy driver for audio driver (which is last), also in no threaded mode.
audio_driver_idx = AudioDriverManager::get_driver_count() - 1;
AudioDriverDummy::get_dummy_singleton()->set_use_threads(false);
@@ -1609,7 +1608,7 @@ error:
display_driver = "";
audio_driver = "";
tablet_driver = "";
- write_movie_path = "";
+ Engine::get_singleton()->set_write_movie_path(String());
project_path = "";
args.clear();
@@ -1784,11 +1783,11 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
rendering_server->set_print_gpu_profile(true);
}
- if (write_movie_path != String()) {
- movie_writer = MovieWriter::find_writer_for_file(write_movie_path);
+ if (Engine::get_singleton()->get_write_movie_path() != String()) {
+ movie_writer = MovieWriter::find_writer_for_file(Engine::get_singleton()->get_write_movie_path());
if (movie_writer == nullptr) {
- ERR_PRINT("Can't find movie writer for file type, aborting: " + write_movie_path);
- write_movie_path = String();
+ ERR_PRINT("Can't find movie writer for file type, aborting: " + Engine::get_singleton()->get_write_movie_path());
+ Engine::get_singleton()->set_write_movie_path(String());
}
}
@@ -2724,7 +2723,7 @@ bool Main::start() {
OS::get_singleton()->set_main_loop(main_loop);
if (movie_writer) {
- movie_writer->begin(DisplayServer::get_singleton()->window_get_size(), fixed_fps, write_movie_path);
+ movie_writer->begin(DisplayServer::get_singleton()->window_get_size(), fixed_fps, Engine::get_singleton()->get_write_movie_path());
}
if (minimum_time_msec) {
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 2007dc6405..76bf71387e 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -1201,11 +1201,15 @@ void AnimationPlayer::_animation_process(double p_delta) {
emit_signal(SceneStringNames::get_singleton()->animation_changed, old, new_name);
}
} else {
- //stop();
playing = false;
_set_process(false);
if (end_notify) {
emit_signal(SceneStringNames::get_singleton()->animation_finished, playback.assigned);
+
+ if (movie_quit_on_finish && OS::get_singleton()->has_feature("movie")) {
+ print_line(vformat("Movie Maker mode is enabled. Quitting on animation finish as requested by: %s", get_path()));
+ get_tree()->quit();
+ }
}
}
end_reached = false;
@@ -1892,6 +1896,14 @@ AnimationPlayer::AnimationMethodCallMode AnimationPlayer::get_method_call_mode()
return method_call_mode;
}
+void AnimationPlayer::set_movie_quit_on_finish_enabled(bool p_enabled) {
+ movie_quit_on_finish = p_enabled;
+}
+
+bool AnimationPlayer::is_movie_quit_on_finish_enabled() const {
+ return movie_quit_on_finish;
+}
+
void AnimationPlayer::_set_process(bool p_process, bool p_force) {
if (processing == p_process && !p_force) {
return;
@@ -2112,6 +2124,9 @@ void AnimationPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_method_call_mode", "mode"), &AnimationPlayer::set_method_call_mode);
ClassDB::bind_method(D_METHOD("get_method_call_mode"), &AnimationPlayer::get_method_call_mode);
+ ClassDB::bind_method(D_METHOD("set_movie_quit_on_finish_enabled"), &AnimationPlayer::set_movie_quit_on_finish_enabled);
+ ClassDB::bind_method(D_METHOD("is_movie_quit_on_finish_enabled"), &AnimationPlayer::is_movie_quit_on_finish_enabled);
+
ClassDB::bind_method(D_METHOD("get_current_animation_position"), &AnimationPlayer::get_current_animation_position);
ClassDB::bind_method(D_METHOD("get_current_animation_length"), &AnimationPlayer::get_current_animation_length);
@@ -2133,6 +2148,8 @@ void AnimationPlayer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_speed", PROPERTY_HINT_RANGE, "-64,64,0.01"), "set_speed_scale", "get_speed_scale");
ADD_PROPERTY(PropertyInfo(Variant::INT, "method_call_mode", PROPERTY_HINT_ENUM, "Deferred,Immediate"), "set_method_call_mode", "get_method_call_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "movie_quit_on_finish"), "set_movie_quit_on_finish_enabled", "is_movie_quit_on_finish_enabled");
+
ADD_SIGNAL(MethodInfo("animation_finished", PropertyInfo(Variant::STRING_NAME, "anim_name")));
ADD_SIGNAL(MethodInfo("animation_changed", PropertyInfo(Variant::STRING_NAME, "old_name"), PropertyInfo(Variant::STRING_NAME, "new_name")));
ADD_SIGNAL(MethodInfo("animation_started", PropertyInfo(Variant::STRING_NAME, "anim_name")));
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index 4dd4b43145..b6d8dab1ed 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -262,6 +262,7 @@ private:
bool reset_on_save = true;
AnimationProcessCallback process_callback = ANIMATION_PROCESS_IDLE;
AnimationMethodCallMode method_call_mode = ANIMATION_METHOD_CALL_DEFERRED;
+ bool movie_quit_on_finish = false;
bool processing = false;
bool active = true;
@@ -373,6 +374,9 @@ public:
void set_method_call_mode(AnimationMethodCallMode p_mode);
AnimationMethodCallMode get_method_call_mode() const;
+ void set_movie_quit_on_finish_enabled(bool p_enabled);
+ bool is_movie_quit_on_finish_enabled() const;
+
void seek(double p_time, bool p_update = false);
void seek_delta(double p_time, float p_delta);
float get_current_animation_position() const;
diff --git a/servers/movie_writer/movie_writer.cpp b/servers/movie_writer/movie_writer.cpp
index 93f9f8ea08..40b2b2539e 100644
--- a/servers/movie_writer/movie_writer.cpp
+++ b/servers/movie_writer/movie_writer.cpp
@@ -31,6 +31,7 @@
#include "movie_writer.h"
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
+#include "core/os/time.h"
#include "servers/display_server.h"
MovieWriter *MovieWriter::writers[MovieWriter::MAX_WRITERS];
@@ -183,4 +184,29 @@ void MovieWriter::add_frame(const Ref<Image> &p_image) {
void MovieWriter::end() {
write_end();
+
+ // Print a report with various statistics.
+ print_line("----------------");
+ String movie_path = Engine::get_singleton()->get_write_movie_path();
+ if (movie_path.is_relative_path()) {
+ // Print absolute path to make finding the file easier,
+ // and to make it clickable in terminal emulators that support this.
+ movie_path = ProjectSettings::get_singleton()->globalize_path("res://").plus_file(movie_path);
+ }
+ print_line(vformat("Done recording movie at path: %s", movie_path));
+
+ const int movie_time_seconds = Engine::get_singleton()->get_frames_drawn() / fps;
+ const String movie_time = vformat("%s:%s:%s",
+ String::num(movie_time_seconds / 3600).pad_zeros(2),
+ String::num((movie_time_seconds % 3600) / 60).pad_zeros(2),
+ String::num(movie_time_seconds % 60).pad_zeros(2));
+
+ const int real_time_seconds = Time::get_singleton()->get_ticks_msec() / 1000;
+ const String real_time = vformat("%s:%s:%s",
+ String::num(real_time_seconds / 3600).pad_zeros(2),
+ String::num((real_time_seconds % 3600) / 60).pad_zeros(2),
+ String::num(real_time_seconds % 60).pad_zeros(2));
+
+ print_line(vformat("%d frames at %d FPS (movie length: %s), recorded in %s (%d%% of real-time speed).", Engine::get_singleton()->get_frames_drawn(), fps, movie_time, real_time, (float(movie_time_seconds) / real_time_seconds) * 100));
+ print_line("----------------");
}