From 5786516d4d6b517e6a28aec191f944aecf2f544d Mon Sep 17 00:00:00 2001 From: reduz Date: Fri, 17 Jun 2022 00:55:19 +0200 Subject: Implement Running Godot as Movie Writer * Allows running the game in "movie writer" mode. * It ensures entirely stable framerate, so your run can be saved stable and with proper sound (which is impossible if your CPU/GPU can't sustain doing this in real-time). * If disabling vsync, it can save movies faster than the game is run, but if you want to control the interaction it can get difficult. * Implements a simple, default MJPEG writer. This new features has two main use cases, which have high demand: * Saving game videos in high quality and ensuring the frame rate is *completely* stable, always. * Using Godot as a tool to make movies and animations (which is ideal if you want interaction, or creating them procedurally. No other software is as good for this). **Note**: This feature **IS NOT** for capturing real-time footage. Use something like OBS, SimpleScreenRecorder or FRAPS to achieve that, as they do a much better job at intercepting the compositor than Godot can probably do using Vulkan or OpenGL natively. If your game runs near real-time when capturing, you can still use this feature but it will play no sound (sound will be saved directly). Usage: $ godot --write-movie movie.avi [scene_file.tscn] Missing: * Options for configuring video writing via GLOBAL_DEF * UI Menu for launching with this mode from the editor. * Add to list of command line options. * Add a feature tag to override configurations when movie writing (fantastic for saving videos with highest quality settings). --- core/config/project_settings.cpp | 1 - core/core_constants.cpp | 1 + core/io/image.cpp | 20 ++++++++++++++++++++ core/io/image.h | 6 ++++++ core/object/object.h | 1 + core/os/os.cpp | 4 ++++ core/os/os.h | 1 + 7 files changed, 33 insertions(+), 1 deletion(-) (limited to 'core') diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 12d936d456..570dc675a8 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -41,7 +41,6 @@ #include "core/os/keyboard.h" #include "core/variant/variant_parser.h" #include "core/version.h" - #include "modules/modules_enabled.gen.h" // For mono. const String ProjectSettings::PROJECT_DATA_DIR_NAME_SUFFIX = "godot"; diff --git a/core/core_constants.cpp b/core/core_constants.cpp index 4f22c99656..3373f4f1e5 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -588,6 +588,7 @@ void register_global_constants() { BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_OBJECT_TOO_BIG); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_PATH_VALID_TYPES); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_SAVE_FILE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_GLOBAL_SAVE_FILE); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_OBJECTID); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_POINTER); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ARRAY_TYPE); diff --git a/core/io/image.cpp b/core/io/image.cpp index dfba45c4e9..6585fd9adf 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -81,9 +81,11 @@ const char *Image::format_names[Image::FORMAT_MAX] = { }; SavePNGFunc Image::save_png_func = nullptr; +SaveJPGFunc Image::save_jpg_func = nullptr; SaveEXRFunc Image::save_exr_func = nullptr; SavePNGBufferFunc Image::save_png_buffer_func = nullptr; +SaveJPGBufferFunc Image::save_jpg_buffer_func = nullptr; void Image::_put_pixelb(int p_x, int p_y, uint32_t p_pixel_size, uint8_t *p_data, const uint8_t *p_pixel) { uint32_t ofs = (p_y * width + p_x) * p_pixel_size; @@ -2286,6 +2288,14 @@ Error Image::save_png(const String &p_path) const { return save_png_func(p_path, Ref((Image *)this)); } +Error Image::save_jpg(const String &p_path, float p_quality) const { + if (save_jpg_func == nullptr) { + return ERR_UNAVAILABLE; + } + + return save_jpg_func(p_path, Ref((Image *)this), p_quality); +} + Vector Image::save_png_to_buffer() const { if (save_png_buffer_func == nullptr) { return Vector(); @@ -2294,6 +2304,14 @@ Vector Image::save_png_to_buffer() const { return save_png_buffer_func(Ref((Image *)this)); } +Vector Image::save_jpg_to_buffer(float p_quality) const { + if (save_jpg_buffer_func == nullptr) { + return Vector(); + } + + return save_jpg_buffer_func(Ref((Image *)this), p_quality); +} + Error Image::save_exr(const String &p_path, bool p_grayscale) const { if (save_exr_func == nullptr) { return ERR_UNAVAILABLE; @@ -3138,6 +3156,8 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("load", "path"), &Image::load); ClassDB::bind_method(D_METHOD("save_png", "path"), &Image::save_png); ClassDB::bind_method(D_METHOD("save_png_to_buffer"), &Image::save_png_to_buffer); + ClassDB::bind_method(D_METHOD("save_jpg", "path", "quality"), &Image::save_jpg, DEFVAL(0.75)); + ClassDB::bind_method(D_METHOD("save_jpg_to_buffer", "quality"), &Image::save_jpg_to_buffer, DEFVAL(0.75)); ClassDB::bind_method(D_METHOD("save_exr", "path", "grayscale"), &Image::save_exr, DEFVAL(false)); ClassDB::bind_method(D_METHOD("detect_alpha"), &Image::detect_alpha); diff --git a/core/io/image.h b/core/io/image.h index 1025554d51..88b80dc984 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -45,6 +45,8 @@ class Image; typedef Error (*SavePNGFunc)(const String &p_path, const Ref &p_img); typedef Vector (*SavePNGBufferFunc)(const Ref &p_img); +typedef Error (*SaveJPGFunc)(const String &p_path, const Ref &p_img, float p_quality); +typedef Vector (*SaveJPGBufferFunc)(const Ref &p_img, float p_quality); typedef Ref (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size); typedef Error (*SaveEXRFunc)(const String &p_path, const Ref &p_img, bool p_grayscale); @@ -54,8 +56,10 @@ class Image : public Resource { public: static SavePNGFunc save_png_func; + static SaveJPGFunc save_jpg_func; static SaveEXRFunc save_exr_func; static SavePNGBufferFunc save_png_buffer_func; + static SaveJPGBufferFunc save_jpg_buffer_func; enum { MAX_WIDTH = (1 << 24), // force a limit somehow @@ -281,7 +285,9 @@ public: Error load(const String &p_path); Error save_png(const String &p_path) const; + Error save_jpg(const String &p_path, float p_quality = 0.75) const; Vector save_png_to_buffer() const; + Vector save_jpg_to_buffer(float p_quality = 0.75) const; Error save_exr(const String &p_path, bool p_grayscale) const; void create_empty(int p_width, int p_height, bool p_use_mipmaps, Format p_format) { diff --git a/core/object/object.h b/core/object/object.h index 7cbedd29d9..d0c6c0cb01 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -85,6 +85,7 @@ enum PropertyHint { PROPERTY_HINT_OBJECT_TOO_BIG, ///< object is too big to send PROPERTY_HINT_NODE_PATH_VALID_TYPES, PROPERTY_HINT_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog + PROPERTY_HINT_GLOBAL_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog PROPERTY_HINT_INT_IS_OBJECTID, PROPERTY_HINT_ARRAY_TYPE, PROPERTY_HINT_INT_IS_POINTER, diff --git a/core/os/os.cpp b/core/os/os.cpp index 327f1c95f2..93477f4288 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -388,6 +388,10 @@ bool OS::has_feature(const String &p_feature) { return true; } + if (p_feature == "movie") { + return _writing_movie; + } + #ifdef DEBUG_ENABLED if (p_feature == "debug") { return true; diff --git a/core/os/os.h b/core/os/os.h index 157b8ab992..c6ea9d869a 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -58,6 +58,7 @@ class OS { bool _allow_layered = false; bool _stdout_enabled = true; bool _stderr_enabled = true; + bool _writing_movie = false; CompositeLogger *_logger = nullptr; -- cgit v1.2.3