diff options
-rw-r--r-- | doc/classes/ProjectSettings.xml | 6 | ||||
-rw-r--r-- | doc/classes/SceneTree.xml | 3 | ||||
-rw-r--r-- | editor/editor_help.cpp | 80 | ||||
-rw-r--r-- | editor/editor_help.h | 2 | ||||
-rw-r--r-- | editor/editor_run.cpp | 5 | ||||
-rw-r--r-- | editor/plugins/debugger_editor_plugin.cpp | 13 | ||||
-rw-r--r-- | editor/plugins/debugger_editor_plugin.h | 1 | ||||
-rw-r--r-- | main/main.cpp | 7 | ||||
-rw-r--r-- | scene/2d/navigation_agent_2d.cpp | 9 | ||||
-rw-r--r-- | scene/2d/path_2d.cpp | 14 | ||||
-rw-r--r-- | scene/3d/navigation_agent_3d.cpp | 11 | ||||
-rw-r--r-- | scene/3d/path_3d.cpp | 90 | ||||
-rw-r--r-- | scene/3d/path_3d.h | 11 | ||||
-rw-r--r-- | scene/main/scene_tree.cpp | 46 | ||||
-rw-r--r-- | scene/main/scene_tree.h | 17 | ||||
-rw-r--r-- | servers/movie_writer/movie_writer.cpp | 137 | ||||
-rw-r--r-- | servers/movie_writer/movie_writer.h | 37 | ||||
-rw-r--r-- | servers/movie_writer/movie_writer_mjpeg.h | 2 | ||||
-rw-r--r-- | servers/movie_writer/movie_writer_pngwav.cpp | 168 | ||||
-rw-r--r-- | servers/movie_writer/movie_writer_pngwav.h | 71 | ||||
-rw-r--r-- | servers/register_server_types.cpp | 1 |
21 files changed, 516 insertions, 215 deletions
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 2391cb892c..553005c4c4 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -494,6 +494,12 @@ <member name="debug/shapes/navigation/geometry_color" type="Color" setter="" getter="" default="Color(0.1, 1, 0.7, 0.4)"> Color of the navigation geometry, visible when "Visible Navigation" is enabled in the Debug menu. </member> + <member name="debug/shapes/paths/geometry_color" type="Color" setter="" getter="" default="Color(0.1, 1, 0.7, 0.4)"> + Color of the curve path geometry, visible when "Visible Paths" is enabled in the Debug menu. + </member> + <member name="debug/shapes/paths/geometry_width" type="float" setter="" getter="" default="2.0"> + Line width of the curve path geometry, visible when "Visible Paths" is enabled in the Debug menu. + </member> <member name="display/mouse_cursor/custom_image" type="String" setter="" getter="" default=""""> Custom image for the mouse cursor (limited to 256×256). </member> diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml index c6b80f171a..cf6ab977db 100644 --- a/doc/classes/SceneTree.xml +++ b/doc/classes/SceneTree.xml @@ -221,6 +221,9 @@ <member name="debug_navigation_hint" type="bool" setter="set_debug_navigation_hint" getter="is_debugging_navigation_hint" default="false"> If [code]true[/code], navigation polygons will be visible when running the game from the editor for debugging purposes. </member> + <member name="debug_paths_hint" type="bool" setter="set_debug_paths_hint" getter="is_debugging_paths_hint" default="false"> + If [code]true[/code], curves from [Path2D] and [Path3D] nodes will be visible when running the game from the editor for debugging purposes. + </member> <member name="edited_scene_root" type="Node" setter="set_edited_scene_root" getter="get_edited_scene_root"> The root of the edited scene. </member> diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index b4325f09c5..36360954d9 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -63,6 +63,8 @@ void EditorHelp::_update_theme() { doc_bold_font = get_theme_font(SNAME("doc_bold"), SNAME("EditorFonts")); doc_title_font = get_theme_font(SNAME("doc_title"), SNAME("EditorFonts")); doc_code_font = get_theme_font(SNAME("doc_source"), SNAME("EditorFonts")); + + doc_title_font_size = get_theme_font_size(SNAME("doc_title_size"), SNAME("EditorFonts")); } void EditorHelp::_search(bool p_search_previous) { @@ -362,8 +364,9 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) { void EditorHelp::_update_method_list(const Vector<DocData::MethodDoc> p_methods, bool &r_method_descrpitons) { Ref<Font> doc_code_font = get_theme_font(SNAME("doc_source"), SNAME("EditorFonts")); - class_desc->pop(); - class_desc->pop(); + class_desc->pop(); // title font size + class_desc->pop(); // title font + class_desc->pop(); // title color class_desc->add_newline(); class_desc->push_font(doc_code_font); @@ -431,8 +434,9 @@ void EditorHelp::_update_method_descriptions(const DocData::ClassDoc p_classdoc, Ref<Font> doc_bold_font = get_theme_font(SNAME("doc_bold"), SNAME("EditorFonts")); Ref<Font> doc_code_font = get_theme_font(SNAME("doc_source"), SNAME("EditorFonts")); String link_color_text = title_color.to_html(false); - class_desc->pop(); - class_desc->pop(); + class_desc->pop(); // title font size + class_desc->pop(); // title font + class_desc->pop(); // title color class_desc->add_newline(); class_desc->add_newline(); @@ -535,15 +539,17 @@ void EditorHelp::_update_doc() { // Class name section_line.push_back(Pair<String, int>(TTR("Top"), 0)); class_desc->push_font(doc_title_font); + class_desc->push_font_size(doc_title_font_size); class_desc->push_color(title_color); class_desc->add_text(TTR("Class:") + " "); class_desc->add_image(icon, icon->get_width(), icon->get_height()); class_desc->add_text(" "); class_desc->push_color(headline_color); _add_text(edited_class); - class_desc->pop(); - class_desc->pop(); - class_desc->pop(); + class_desc->pop(); // color + class_desc->pop(); // color + class_desc->pop(); // font size + class_desc->pop(); // font class_desc->add_newline(); // Inheritance tree @@ -624,9 +630,11 @@ void EditorHelp::_update_doc() { description_line = class_desc->get_paragraph_count() - 2; class_desc->push_color(title_color); class_desc->push_font(doc_title_font); + class_desc->push_font_size(doc_title_font_size); class_desc->add_text(TTR("Description")); - class_desc->pop(); - class_desc->pop(); + class_desc->pop(); // font size + class_desc->pop(); // font + class_desc->pop(); // color class_desc->add_newline(); class_desc->add_newline(); @@ -646,9 +654,11 @@ void EditorHelp::_update_doc() { if (cd.tutorials.size()) { class_desc->push_color(title_color); class_desc->push_font(doc_title_font); + class_desc->push_font_size(doc_title_font_size); class_desc->add_text(TTR("Online Tutorials")); - class_desc->pop(); - class_desc->pop(); + class_desc->pop(); // font size + class_desc->pop(); // font + class_desc->pop(); // color class_desc->push_indent(1); class_desc->push_font(doc_code_font); @@ -694,9 +704,11 @@ void EditorHelp::_update_doc() { section_line.push_back(Pair<String, int>(TTR("Properties"), class_desc->get_paragraph_count() - 2)); class_desc->push_color(title_color); class_desc->push_font(doc_title_font); + class_desc->push_font_size(doc_title_font_size); class_desc->add_text(TTR("Properties")); - class_desc->pop(); - class_desc->pop(); + class_desc->pop(); // font size + class_desc->pop(); // font + class_desc->pop(); // color class_desc->add_newline(); class_desc->push_font(doc_code_font); @@ -858,6 +870,7 @@ void EditorHelp::_update_doc() { section_line.push_back(Pair<String, int>(TTR("Constructors"), class_desc->get_paragraph_count() - 2)); class_desc->push_color(title_color); class_desc->push_font(doc_title_font); + class_desc->push_font_size(doc_title_font_size); class_desc->add_text(TTR("Constructors")); _update_method_list(cd.constructors, constructor_descriptions); } @@ -869,6 +882,7 @@ void EditorHelp::_update_doc() { section_line.push_back(Pair<String, int>(TTR("Methods"), class_desc->get_paragraph_count() - 2)); class_desc->push_color(title_color); class_desc->push_font(doc_title_font); + class_desc->push_font_size(doc_title_font_size); class_desc->add_text(TTR("Methods")); _update_method_list(methods, method_descriptions); } @@ -881,6 +895,7 @@ void EditorHelp::_update_doc() { section_line.push_back(Pair<String, int>(TTR("Operators"), class_desc->get_paragraph_count() - 2)); class_desc->push_color(title_color); class_desc->push_font(doc_title_font); + class_desc->push_font_size(doc_title_font_size); class_desc->add_text(TTR("Operators")); _update_method_list(cd.operators, operator_descriptions); } @@ -890,9 +905,11 @@ void EditorHelp::_update_doc() { section_line.push_back(Pair<String, int>(TTR("Theme Properties"), class_desc->get_paragraph_count() - 2)); class_desc->push_color(title_color); class_desc->push_font(doc_title_font); + class_desc->push_font_size(doc_title_font_size); class_desc->add_text(TTR("Theme Properties")); - class_desc->pop(); - class_desc->pop(); + class_desc->pop(); // font size + class_desc->pop(); // font + class_desc->pop(); // color class_desc->add_newline(); class_desc->add_newline(); @@ -916,13 +933,15 @@ void EditorHelp::_update_doc() { class_desc->push_color(title_color); class_desc->push_font(doc_title_font); + class_desc->push_font_size(doc_title_font_size); if (data_type_names.has(theme_data_type)) { class_desc->add_text(data_type_names[theme_data_type]); } else { class_desc->add_text(""); } - class_desc->pop(); - class_desc->pop(); + class_desc->pop(); // font size + class_desc->pop(); // font + class_desc->pop(); // color class_desc->add_newline(); class_desc->add_newline(); @@ -984,9 +1003,11 @@ void EditorHelp::_update_doc() { section_line.push_back(Pair<String, int>(TTR("Signals"), class_desc->get_paragraph_count() - 2)); class_desc->push_color(title_color); class_desc->push_font(doc_title_font); + class_desc->push_font_size(doc_title_font_size); class_desc->add_text(TTR("Signals")); - class_desc->pop(); - class_desc->pop(); + class_desc->pop(); // font size + class_desc->pop(); // font + class_desc->pop(); // color class_desc->add_newline(); class_desc->add_newline(); @@ -1070,9 +1091,11 @@ void EditorHelp::_update_doc() { section_line.push_back(Pair<String, int>(TTR("Enumerations"), class_desc->get_paragraph_count() - 2)); class_desc->push_color(title_color); class_desc->push_font(doc_title_font); + class_desc->push_font_size(doc_title_font_size); class_desc->add_text(TTR("Enumerations")); - class_desc->pop(); - class_desc->pop(); + class_desc->pop(); // font size + class_desc->pop(); // font + class_desc->pop(); // color class_desc->push_indent(1); class_desc->add_newline(); @@ -1174,9 +1197,11 @@ void EditorHelp::_update_doc() { section_line.push_back(Pair<String, int>(TTR("Constants"), class_desc->get_paragraph_count() - 2)); class_desc->push_color(title_color); class_desc->push_font(doc_title_font); + class_desc->push_font_size(doc_title_font_size); class_desc->add_text(TTR("Constants")); - class_desc->pop(); - class_desc->pop(); + class_desc->pop(); // font size + class_desc->pop(); // font + class_desc->pop(); // color class_desc->push_indent(1); class_desc->add_newline(); @@ -1235,9 +1260,11 @@ void EditorHelp::_update_doc() { section_line.push_back(Pair<String, int>(TTR("Property Descriptions"), class_desc->get_paragraph_count() - 2)); class_desc->push_color(title_color); class_desc->push_font(doc_title_font); + class_desc->push_font_size(doc_title_font_size); class_desc->add_text(TTR("Property Descriptions")); - class_desc->pop(); - class_desc->pop(); + class_desc->pop(); // font size + class_desc->pop(); // font + class_desc->pop(); // color class_desc->add_newline(); class_desc->add_newline(); @@ -1401,6 +1428,7 @@ void EditorHelp::_update_doc() { section_line.push_back(Pair<String, int>(TTR("Constructor Descriptions"), class_desc->get_paragraph_count() - 2)); class_desc->push_color(title_color); class_desc->push_font(doc_title_font); + class_desc->push_font_size(doc_title_font_size); class_desc->add_text(TTR("Constructor Descriptions")); _update_method_descriptions(cd, cd.constructors, "constructor"); } @@ -1410,6 +1438,7 @@ void EditorHelp::_update_doc() { section_line.push_back(Pair<String, int>(TTR("Method Descriptions"), class_desc->get_paragraph_count() - 2)); class_desc->push_color(title_color); class_desc->push_font(doc_title_font); + class_desc->push_font_size(doc_title_font_size); class_desc->add_text(TTR("Method Descriptions")); _update_method_descriptions(cd, methods, "method"); } @@ -1419,6 +1448,7 @@ void EditorHelp::_update_doc() { section_line.push_back(Pair<String, int>(TTR("Operator Descriptions"), class_desc->get_paragraph_count() - 2)); class_desc->push_color(title_color); class_desc->push_font(doc_title_font); + class_desc->push_font_size(doc_title_font_size); class_desc->add_text(TTR("Operator Descriptions")); _update_method_descriptions(cd, cd.operators, "operator"); } diff --git a/editor/editor_help.h b/editor/editor_help.h index 766a09f485..7f91a8102d 100644 --- a/editor/editor_help.h +++ b/editor/editor_help.h @@ -140,6 +140,8 @@ class EditorHelp : public VBoxContainer { Ref<Font> doc_title_font; Ref<Font> doc_code_font; + int doc_title_font_size; + int scroll_to = -1; void _update_theme(); diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp index 04a3bf2915..ba49c6dc5f 100644 --- a/editor/editor_run.cpp +++ b/editor/editor_run.cpp @@ -59,11 +59,16 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie) { args.push_back(itos(OS::get_singleton()->get_process_id())); bool debug_collisions = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_collisons", false); + bool debug_paths = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_paths", false); bool debug_navigation = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_navigation", false); if (debug_collisions) { args.push_back("--debug-collisions"); } + if (debug_paths) { + args.push_back("--debug-paths"); + } + if (debug_navigation) { args.push_back("--debug-navigation"); } diff --git a/editor/plugins/debugger_editor_plugin.cpp b/editor/plugins/debugger_editor_plugin.cpp index 8ea50c4529..5c90d70982 100644 --- a/editor/plugins/debugger_editor_plugin.cpp +++ b/editor/plugins/debugger_editor_plugin.cpp @@ -72,6 +72,9 @@ DebuggerEditorPlugin::DebuggerEditorPlugin(MenuButton *p_debug_menu) { p->add_check_shortcut(ED_SHORTCUT("editor/visible_collision_shapes", TTR("Visible Collision Shapes")), RUN_DEBUG_COLLISONS); p->set_item_tooltip(-1, TTR("When this option is enabled, collision shapes and raycast nodes (for 2D and 3D) will be visible in the running project.")); + p->add_check_shortcut(ED_SHORTCUT("editor/visible_paths", TTR("Visible Paths")), RUN_DEBUG_PATHS); + p->set_item_tooltip(-1, + TTR("When this option is enabled, curve resources used by path nodes will be visible in the running project.")); p->add_check_shortcut(ED_SHORTCUT("editor/visible_navigation", TTR("Visible Navigation")), RUN_DEBUG_NAVIGATION); p->set_item_tooltip(-1, TTR("When this option is enabled, navigation meshes and polygons will be visible in the running project.")); @@ -153,6 +156,12 @@ void DebuggerEditorPlugin::_menu_option(int p_option) { EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_collisons", !ischecked); } break; + case RUN_DEBUG_PATHS: { + bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_PATHS)); + debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_PATHS), !ischecked); + EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_paths", !ischecked); + + } break; case RUN_DEBUG_NAVIGATION: { bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_NAVIGATION)); debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_NAVIGATION), !ischecked); @@ -182,6 +191,7 @@ void DebuggerEditorPlugin::_update_debug_options() { bool check_deploy_remote = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_deploy_remote_debug", false); bool check_file_server = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_file_server", false); bool check_debug_collisions = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_collisons", false); + bool check_debug_paths = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_paths", false); bool check_debug_navigation = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_navigation", false); bool check_live_debug = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_live_debug", true); bool check_reload_scripts = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_reload_scripts", true); @@ -196,6 +206,9 @@ void DebuggerEditorPlugin::_update_debug_options() { if (check_debug_collisions) { _menu_option(RUN_DEBUG_COLLISONS); } + if (check_debug_paths) { + _menu_option(RUN_DEBUG_PATHS); + } if (check_debug_navigation) { _menu_option(RUN_DEBUG_NAVIGATION); } diff --git a/editor/plugins/debugger_editor_plugin.h b/editor/plugins/debugger_editor_plugin.h index 10e1a27933..fb963385cd 100644 --- a/editor/plugins/debugger_editor_plugin.h +++ b/editor/plugins/debugger_editor_plugin.h @@ -49,6 +49,7 @@ private: RUN_FILE_SERVER, RUN_LIVE_DEBUG, RUN_DEBUG_COLLISONS, + RUN_DEBUG_PATHS, RUN_DEBUG_NAVIGATION, RUN_DEPLOY_REMOTE_DEBUG, RUN_RELOAD_SCRIPTS, diff --git a/main/main.cpp b/main/main.cpp index bfb0eacdfc..f3c56c7205 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -175,6 +175,7 @@ static Vector2 init_custom_pos; static bool use_debug_profiler = false; #ifdef DEBUG_ENABLED static bool debug_collisions = false; +static bool debug_paths = false; static bool debug_navigation = false; #endif static int frame_delay = 0; @@ -357,6 +358,7 @@ void Main::print_help(const char *p_binary) { OS::get_singleton()->print(" --remote-debug <uri> Remote debug (<protocol>://<host/IP>[:<port>], e.g. tcp://127.0.0.1:6007).\n"); #if defined(DEBUG_ENABLED) OS::get_singleton()->print(" --debug-collisions Show collision shapes when running the scene.\n"); + OS::get_singleton()->print(" --debug-paths Show path lines when running the scene.\n"); OS::get_singleton()->print(" --debug-navigation Show navigation polygons when running the scene.\n"); OS::get_singleton()->print(" --debug-stringnames Print all StringName allocations to stdout when the engine quits.\n"); #endif @@ -1107,6 +1109,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph #if defined(DEBUG_ENABLED) } else if (I->get() == "--debug-collisions") { debug_collisions = true; + } else if (I->get() == "--debug-paths") { + debug_paths = true; } else if (I->get() == "--debug-navigation") { debug_navigation = true; } else if (I->get() == "--debug-stringnames") { @@ -2375,6 +2379,9 @@ bool Main::start() { if (debug_collisions) { sml->set_debug_collisions_hint(true); } + if (debug_paths) { + sml->set_debug_paths_hint(true); + } if (debug_navigation) { sml->set_debug_navigation_hint(true); } diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index 5a451a6dab..a5f7faffef 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -87,16 +87,19 @@ void NavigationAgent2D::_bind_methods() { ClassDB::bind_method(D_METHOD("_avoidance_done", "new_velocity"), &NavigationAgent2D::_avoidance_done); + ADD_GROUP("Pathfinding", ""); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01,suffix:px"), "set_path_desired_distance", "get_path_desired_distance"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01,suffix:px"), "set_target_desired_distance", "get_target_desired_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "10,100,1,suffix:px"), "set_path_max_distance", "get_path_max_distance"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); + + ADD_GROUP("Avoidance", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.1,500,0.01,suffix:px"), "set_radius", "get_radius"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "neighbor_dist", PROPERTY_HINT_RANGE, "0.1,100000,0.01,suffix:px"), "set_neighbor_dist", "get_neighbor_dist"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_neighbors", PROPERTY_HINT_RANGE, "1,10000,1"), "set_max_neighbors", "get_max_neighbors"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_horizon", PROPERTY_HINT_RANGE, "0.1,10000,0.01,suffix:s"), "set_time_horizon", "get_time_horizon"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_speed", PROPERTY_HINT_RANGE, "0.1,100000,0.01,suffix:px/s"), "set_max_speed", "get_max_speed"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "10,100,1,suffix:px"), "set_path_max_distance", "get_path_max_distance"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); ADD_SIGNAL(MethodInfo("path_changed")); ADD_SIGNAL(MethodInfo("target_reached")); diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index ba90a275e6..8eb48ffb30 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -87,13 +87,13 @@ bool Path2D::_edit_is_selected_on_click(const Point2 &p_point, double p_toleranc void Path2D::_notification(int p_what) { switch (p_what) { - // Draw the curve if navigation debugging is enabled. + // Draw the curve if path debugging is enabled. case NOTIFICATION_DRAW: { if (!curve.is_valid()) { break; } - if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_navigation_hint()) { + if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_paths_hint()) { return; } @@ -102,12 +102,10 @@ void Path2D::_notification(int p_what) { } #ifdef TOOLS_ENABLED - const real_t line_width = 2 * EDSCALE; + const real_t line_width = get_tree()->get_debug_paths_width() * EDSCALE; #else - const real_t line_width = 2; + const real_t line_width = get_tree()->get_debug_paths_width(); #endif - const Color color = Color(0.5, 0.6, 1.0, 0.7); - _cached_draw_pts.resize(curve->get_point_count() * 8); int count = 0; @@ -119,7 +117,7 @@ void Path2D::_notification(int p_what) { } } - draw_polyline(_cached_draw_pts, color, line_width, true); + draw_polyline(_cached_draw_pts, get_tree()->get_debug_paths_color(), line_width, true); } break; } } @@ -129,7 +127,7 @@ void Path2D::_curve_changed() { return; } - if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_navigation_hint()) { + if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_paths_hint()) { return; } diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index e5ec444335..3752713d6a 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -91,18 +91,21 @@ void NavigationAgent3D::_bind_methods() { ClassDB::bind_method(D_METHOD("_avoidance_done", "new_velocity"), &NavigationAgent3D::_avoidance_done); + ADD_GROUP("Pathfinding", ""); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01,suffix:m"), "set_path_desired_distance", "get_path_desired_distance"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_desired_distance", PROPERTY_HINT_RANGE, "0.1,100,0.01,suffix:m"), "set_target_desired_distance", "get_target_desired_distance"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.1,100,0.01,suffix:m"), "set_radius", "get_radius"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "agent_height_offset", PROPERTY_HINT_RANGE, "-100.0,100,0.01,suffix:m"), "set_agent_height_offset", "get_agent_height_offset"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "0.01,100,0.1,suffix:m"), "set_path_max_distance", "get_path_max_distance"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); + + ADD_GROUP("Avoidance", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.1,100,0.01,suffix:m"), "set_radius", "get_radius"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "neighbor_dist", PROPERTY_HINT_RANGE, "0.1,10000,0.01,suffix:m"), "set_neighbor_dist", "get_neighbor_dist"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_neighbors", PROPERTY_HINT_RANGE, "1,10000,1"), "set_max_neighbors", "get_max_neighbors"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_horizon", PROPERTY_HINT_RANGE, "0.01,100,0.01,suffix:s"), "set_time_horizon", "get_time_horizon"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_speed", PROPERTY_HINT_RANGE, "0.1,10000,0.01,suffix:m/s"), "set_max_speed", "get_max_speed"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "0.01,100,0.1,suffix:m"), "set_path_max_distance", "get_path_max_distance"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_y"), "set_ignore_y", "get_ignore_y"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); ADD_SIGNAL(MethodInfo("path_changed")); ADD_SIGNAL(MethodInfo("target_reached")); diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index e13cf6f9c8..f53e783fbd 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -30,6 +30,92 @@ #include "path_3d.h" +Path3D::Path3D() { + SceneTree *st = SceneTree::get_singleton(); + if (st && st->is_debugging_paths_hint()) { + debug_instance = RS::get_singleton()->instance_create(); + set_notify_transform(true); + _update_debug_mesh(); + } +} + +Path3D::~Path3D() { + if (debug_instance.is_valid()) { + RS::get_singleton()->free(debug_instance); + } + if (debug_mesh.is_valid()) { + RS::get_singleton()->free(debug_mesh->get_rid()); + } +} + +void Path3D::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + SceneTree *st = SceneTree::get_singleton(); + if (st && st->is_debugging_paths_hint()) { + _update_debug_mesh(); + } + } break; + + case NOTIFICATION_EXIT_TREE: { + SceneTree *st = SceneTree::get_singleton(); + if (st && st->is_debugging_paths_hint()) { + RS::get_singleton()->instance_set_visible(debug_instance, false); + } + } break; + + case NOTIFICATION_TRANSFORM_CHANGED: { + if (is_inside_tree() && debug_instance.is_valid()) { + RS::get_singleton()->instance_set_transform(debug_instance, get_global_transform()); + } + } break; + } +} + +void Path3D::_update_debug_mesh() { + SceneTree *st = SceneTree::get_singleton(); + if (!(st && st->is_debugging_paths_hint())) { + return; + } + + if (!debug_mesh.is_valid()) { + debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + } + + if (!(curve.is_valid())) { + RS::get_singleton()->instance_set_visible(debug_instance, false); + return; + } + if (curve->get_point_count() < 2) { + RS::get_singleton()->instance_set_visible(debug_instance, false); + return; + } + + Vector<Vector3> vertex_array; + + for (int i = 1; i < curve->get_point_count(); i++) { + Vector3 line_end = curve->get_point_position(i); + Vector3 line_start = curve->get_point_position(i - 1); + vertex_array.push_back(line_start); + vertex_array.push_back(line_end); + } + + Array mesh_array; + mesh_array.resize(Mesh::ARRAY_MAX); + mesh_array[Mesh::ARRAY_VERTEX] = vertex_array; + + debug_mesh->clear_surfaces(); + debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, mesh_array); + + RS::get_singleton()->instance_set_base(debug_instance, debug_mesh->get_rid()); + RS::get_singleton()->mesh_surface_set_material(debug_mesh->get_rid(), 0, st->get_debug_paths_material()->get_rid()); + if (is_inside_tree()) { + RS::get_singleton()->instance_set_scenario(debug_instance, get_world_3d()->get_scenario()); + RS::get_singleton()->instance_set_transform(debug_instance, get_global_transform()); + RS::get_singleton()->instance_set_visible(debug_instance, is_visible_in_tree()); + } +} + void Path3D::_curve_changed() { if (is_inside_tree() && Engine::get_singleton()->is_editor_hint()) { update_gizmos(); @@ -48,6 +134,10 @@ void Path3D::_curve_changed() { } } } + SceneTree *st = SceneTree::get_singleton(); + if (st && st->is_debugging_paths_hint()) { + _update_debug_mesh(); + } } void Path3D::set_curve(const Ref<Curve3D> &p_curve) { diff --git a/scene/3d/path_3d.h b/scene/3d/path_3d.h index cb67a044d1..7c7284534e 100644 --- a/scene/3d/path_3d.h +++ b/scene/3d/path_3d.h @@ -41,14 +41,23 @@ class Path3D : public Node3D { void _curve_changed(); + RID debug_instance; + Ref<ArrayMesh> debug_mesh; + +private: + void _update_debug_mesh(); + protected: + void _notification(int p_what); + static void _bind_methods(); public: void set_curve(const Ref<Curve3D> &p_curve); Ref<Curve3D> get_curve() const; - Path3D() {} + Path3D(); + ~Path3D(); }; class PathFollow3D : public Node3D { diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index dd77877c7d..18f69ecc82 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -659,6 +659,14 @@ bool SceneTree::is_debugging_collisions_hint() const { return debug_collisions_hint; } +void SceneTree::set_debug_paths_hint(bool p_enabled) { + debug_paths_hint = p_enabled; +} + +bool SceneTree::is_debugging_paths_hint() const { + return debug_paths_hint; +} + void SceneTree::set_debug_navigation_hint(bool p_enabled) { debug_navigation_hint = p_enabled; } @@ -684,6 +692,22 @@ Color SceneTree::get_debug_collision_contact_color() const { return debug_collision_contact_color; } +void SceneTree::set_debug_paths_color(const Color &p_color) { + debug_paths_color = p_color; +} + +Color SceneTree::get_debug_paths_color() const { + return debug_paths_color; +} + +void SceneTree::set_debug_paths_width(float p_width) { + debug_paths_width = p_width; +} + +float SceneTree::get_debug_paths_width() const { + return debug_paths_width; +} + void SceneTree::set_debug_navigation_color(const Color &p_color) { debug_navigation_color = p_color; } @@ -700,6 +724,23 @@ Color SceneTree::get_debug_navigation_disabled_color() const { return debug_navigation_disabled_color; } +Ref<Material> SceneTree::get_debug_paths_material() { + if (debug_paths_material.is_valid()) { + return debug_paths_material; + } + + Ref<StandardMaterial3D> _debug_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); + _debug_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + _debug_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + _debug_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); + _debug_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + _debug_material->set_albedo(get_debug_paths_color()); + + debug_paths_material = _debug_material; + + return debug_paths_material; +} + Ref<Material> SceneTree::get_debug_navigation_material() { if (navigation_material.is_valid()) { return navigation_material; @@ -1207,6 +1248,8 @@ void SceneTree::_bind_methods() { ClassDB::bind_method(D_METHOD("set_debug_collisions_hint", "enable"), &SceneTree::set_debug_collisions_hint); ClassDB::bind_method(D_METHOD("is_debugging_collisions_hint"), &SceneTree::is_debugging_collisions_hint); + ClassDB::bind_method(D_METHOD("set_debug_paths_hint", "enable"), &SceneTree::set_debug_paths_hint); + ClassDB::bind_method(D_METHOD("is_debugging_paths_hint"), &SceneTree::is_debugging_paths_hint); ClassDB::bind_method(D_METHOD("set_debug_navigation_hint", "enable"), &SceneTree::set_debug_navigation_hint); ClassDB::bind_method(D_METHOD("is_debugging_navigation_hint"), &SceneTree::is_debugging_navigation_hint); @@ -1268,6 +1311,7 @@ void SceneTree::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_accept_quit"), "set_auto_accept_quit", "is_auto_accept_quit"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "quit_on_go_back"), "set_quit_on_go_back", "is_quit_on_go_back"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_collisions_hint"), "set_debug_collisions_hint", "is_debugging_collisions_hint"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_paths_hint"), "set_debug_paths_hint", "is_debugging_paths_hint"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_navigation_hint"), "set_debug_navigation_hint", "is_debugging_navigation_hint"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "paused"), "set_pause", "is_paused"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "edited_scene_root", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_edited_scene_root", "get_edited_scene_root"); @@ -1344,6 +1388,8 @@ SceneTree::SceneTree() { } debug_collisions_color = GLOBAL_DEF("debug/shapes/collision/shape_color", Color(0.0, 0.6, 0.7, 0.42)); debug_collision_contact_color = GLOBAL_DEF("debug/shapes/collision/contact_color", Color(1.0, 0.2, 0.1, 0.8)); + debug_paths_color = GLOBAL_DEF("debug/shapes/paths/geometry_color", Color(0.1, 1.0, 0.7, 0.4)); + debug_paths_width = GLOBAL_DEF("debug/shapes/paths/geometry_width", 2.0); debug_navigation_color = GLOBAL_DEF("debug/shapes/navigation/geometry_color", Color(0.1, 1.0, 0.7, 0.4)); debug_navigation_disabled_color = GLOBAL_DEF("debug/shapes/navigation/disabled_geometry_color", Color(1.0, 0.7, 0.1, 0.4)); collision_debug_contacts = GLOBAL_DEF("debug/shapes/collision/max_contacts_displayed", 10000); diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 67a17a69f2..a34aa8e2cd 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -97,6 +97,7 @@ private: #ifdef DEBUG_ENABLED bool debug_collisions_hint = false; + bool debug_paths_hint = false; bool debug_navigation_hint = false; #endif bool paused = false; @@ -146,9 +147,12 @@ private: Color debug_collisions_color; Color debug_collision_contact_color; + Color debug_paths_color; + float debug_paths_width = 1.0f; Color debug_navigation_color; Color debug_navigation_disabled_color; Ref<ArrayMesh> debug_contact_mesh; + Ref<Material> debug_paths_material; Ref<Material> navigation_material; Ref<Material> navigation_disabled_material; Ref<Material> collision_material; @@ -297,12 +301,18 @@ public: void set_debug_collisions_hint(bool p_enabled); bool is_debugging_collisions_hint() const; + void set_debug_paths_hint(bool p_enabled); + bool is_debugging_paths_hint() const; + void set_debug_navigation_hint(bool p_enabled); bool is_debugging_navigation_hint() const; #else void set_debug_collisions_hint(bool p_enabled) {} bool is_debugging_collisions_hint() const { return false; } + void set_debug_paths_hint(bool p_enabled) {} + bool is_debugging_paths_hint() const { return false; } + void set_debug_navigation_hint(bool p_enabled) {} bool is_debugging_navigation_hint() const { return false; } #endif @@ -313,12 +323,19 @@ public: void set_debug_collision_contact_color(const Color &p_color); Color get_debug_collision_contact_color() const; + void set_debug_paths_color(const Color &p_color); + Color get_debug_paths_color() const; + + void set_debug_paths_width(float p_width); + float get_debug_paths_width() const; + void set_debug_navigation_color(const Color &p_color); Color get_debug_navigation_color() const; void set_debug_navigation_disabled_color(const Color &p_color); Color get_debug_navigation_disabled_color() const; + Ref<Material> get_debug_paths_material(); Ref<Material> get_debug_navigation_material(); Ref<Material> get_debug_navigation_disabled_material(); Ref<Material> get_debug_collision_material(); diff --git a/servers/movie_writer/movie_writer.cpp b/servers/movie_writer/movie_writer.cpp index 8560d92aa2..9f96b8cfda 100644 --- a/servers/movie_writer/movie_writer.cpp +++ b/servers/movie_writer/movie_writer.cpp @@ -30,7 +30,6 @@ #include "movie_writer.h" #include "core/config/project_settings.h" -#include "core/io/dir_access.h" MovieWriter *MovieWriter::writers[MovieWriter::MAX_WRITERS]; uint32_t MovieWriter::writer_count = 0; @@ -170,139 +169,3 @@ void MovieWriter::add_frame(const Ref<Image> &p_image) { void MovieWriter::end() { write_end(); } -///////////////////////////////////////// - -uint32_t MovieWriterPNGWAV::get_audio_mix_rate() const { - return mix_rate; -} -AudioServer::SpeakerMode MovieWriterPNGWAV::get_audio_speaker_mode() const { - return speaker_mode; -} - -void MovieWriterPNGWAV::get_supported_extensions(List<String> *r_extensions) const { - r_extensions->push_back("png"); -} - -bool MovieWriterPNGWAV::handles_file(const String &p_path) const { - return p_path.get_extension().to_lower() == "png"; -} - -String MovieWriterPNGWAV::zeros_str(uint32_t p_index) { - char zeros[MAX_TRAILING_ZEROS + 1]; - for (uint32_t i = 0; i < MAX_TRAILING_ZEROS; i++) { - uint32_t idx = MAX_TRAILING_ZEROS - i - 1; - uint32_t digit = (p_index / uint32_t(Math::pow(double(10), double(idx)))) % 10; - zeros[i] = '0' + digit; - } - zeros[MAX_TRAILING_ZEROS] = 0; - return zeros; -} - -Error MovieWriterPNGWAV::write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) { - // Quick & Dirty PNGWAV Code based on - https://docs.microsoft.com/en-us/windows/win32/directshow/avi-riff-file-reference - - base_path = p_base_path.get_basename(); - if (base_path.is_relative_path()) { - base_path = "res://" + base_path; - } - - { - //Remove existing files before writing anew - uint32_t idx = 0; - Ref<DirAccess> d = DirAccess::open(base_path.get_base_dir()); - String file = base_path.get_file(); - while (true) { - String path = file + zeros_str(idx) + ".png"; - if (d->remove(path) != OK) { - break; - } - } - } - - f_wav = FileAccess::open(base_path + ".wav", FileAccess::WRITE_READ); - ERR_FAIL_COND_V(f_wav.is_null(), ERR_CANT_OPEN); - - fps = p_fps; - - f_wav->store_buffer((const uint8_t *)"RIFF", 4); - int total_size = 4 /* WAVE */ + 8 /* fmt+size */ + 16 /* format */ + 8 /* data+size */; - f_wav->store_32(total_size); //will store final later - f_wav->store_buffer((const uint8_t *)"WAVE", 4); - - /* FORMAT CHUNK */ - - f_wav->store_buffer((const uint8_t *)"fmt ", 4); - - uint32_t channels = 2; - switch (speaker_mode) { - case AudioServer::SPEAKER_MODE_STEREO: - channels = 2; - break; - case AudioServer::SPEAKER_SURROUND_31: - channels = 4; - break; - case AudioServer::SPEAKER_SURROUND_51: - channels = 6; - break; - case AudioServer::SPEAKER_SURROUND_71: - channels = 8; - break; - } - - f_wav->store_32(16); //standard format, no extra fields - f_wav->store_16(1); // compression code, standard PCM - f_wav->store_16(channels); //CHANNELS: 2 - - f_wav->store_32(mix_rate); - - /* useless stuff the format asks for */ - - int bits_per_sample = 32; - int blockalign = bits_per_sample / 8 * channels; - int bytes_per_sec = mix_rate * blockalign; - - audio_block_size = (mix_rate / fps) * blockalign; - - f_wav->store_32(bytes_per_sec); - f_wav->store_16(blockalign); // block align (unused) - f_wav->store_16(bits_per_sample); - - /* DATA CHUNK */ - - f_wav->store_buffer((const uint8_t *)"data", 4); - - f_wav->store_32(0); //data size... wooh - wav_data_size_pos = f_wav->get_position(); - - return OK; -} - -Error MovieWriterPNGWAV::write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) { - ERR_FAIL_COND_V(!f_wav.is_valid(), ERR_UNCONFIGURED); - - Vector<uint8_t> png_buffer = p_image->save_png_to_buffer(); - - Ref<FileAccess> fi = FileAccess::open(base_path + zeros_str(frame_count) + ".png", FileAccess::WRITE); - fi->store_buffer(png_buffer.ptr(), png_buffer.size()); - f_wav->store_buffer((const uint8_t *)p_audio_data, audio_block_size); - - frame_count++; - - return OK; -} - -void MovieWriterPNGWAV::write_end() { - if (f_wav.is_valid()) { - uint32_t total_size = 4 /* WAVE */ + 8 /* fmt+size */ + 16 /* format */ + 8 /* data+size */; - uint32_t datasize = f_wav->get_position() - wav_data_size_pos; - f_wav->seek(4); - f_wav->store_32(total_size + datasize); - f_wav->seek(0x28); - f_wav->store_32(datasize); - } -} - -MovieWriterPNGWAV::MovieWriterPNGWAV() { - mix_rate = GLOBAL_GET("editor/movie_writer/mix_rate"); - speaker_mode = AudioServer::SpeakerMode(int(GLOBAL_GET("editor/movie_writer/speaker_mode"))); -} diff --git a/servers/movie_writer/movie_writer.h b/servers/movie_writer/movie_writer.h index 11e739df39..1ec6e93052 100644 --- a/servers/movie_writer/movie_writer.h +++ b/servers/movie_writer/movie_writer.h @@ -85,39 +85,4 @@ public: void end(); }; -class MovieWriterPNGWAV : public MovieWriter { - GDCLASS(MovieWriterPNGWAV, MovieWriter) - - enum { - MAX_TRAILING_ZEROS = 8 // more than 10 days at 60fps, no hard drive can put up with this anyway :) - }; - - uint32_t mix_rate = 48000; - AudioServer::SpeakerMode speaker_mode = AudioServer::SPEAKER_MODE_STEREO; - String base_path; - uint32_t frame_count = 0; - uint32_t fps = 0; - - uint32_t audio_block_size = 0; - - Ref<FileAccess> f_wav; - uint32_t wav_data_size_pos = 0; - - String zeros_str(uint32_t p_index); - -protected: - virtual uint32_t get_audio_mix_rate() const override; - virtual AudioServer::SpeakerMode get_audio_speaker_mode() const override; - virtual void get_supported_extensions(List<String> *r_extensions) const override; - - virtual Error write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) override; - virtual Error write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) override; - virtual void write_end() override; - - virtual bool handles_file(const String &p_path) const override; - -public: - MovieWriterPNGWAV(); -}; - -#endif // VIDEO_WRITER_H +#endif // MOVIE_WRITER_H diff --git a/servers/movie_writer/movie_writer_mjpeg.h b/servers/movie_writer/movie_writer_mjpeg.h index 822bedfedf..233267df30 100644 --- a/servers/movie_writer/movie_writer_mjpeg.h +++ b/servers/movie_writer/movie_writer_mjpeg.h @@ -70,4 +70,4 @@ public: MovieWriterMJPEG(); }; -#endif // MOVIE_WRITER_AVIJPEG_H +#endif // MOVIE_WRITER_MJPEG_H diff --git a/servers/movie_writer/movie_writer_pngwav.cpp b/servers/movie_writer/movie_writer_pngwav.cpp new file mode 100644 index 0000000000..bd79b0bd98 --- /dev/null +++ b/servers/movie_writer/movie_writer_pngwav.cpp @@ -0,0 +1,168 @@ +/*************************************************************************/ +/* movie_writer_pngwav.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "movie_writer_pngwav.h" +#include "core/config/project_settings.h" +#include "core/io/dir_access.h" + +uint32_t MovieWriterPNGWAV::get_audio_mix_rate() const { + return mix_rate; +} +AudioServer::SpeakerMode MovieWriterPNGWAV::get_audio_speaker_mode() const { + return speaker_mode; +} + +void MovieWriterPNGWAV::get_supported_extensions(List<String> *r_extensions) const { + r_extensions->push_back("png"); +} + +bool MovieWriterPNGWAV::handles_file(const String &p_path) const { + return p_path.get_extension().to_lower() == "png"; +} + +String MovieWriterPNGWAV::zeros_str(uint32_t p_index) { + char zeros[MAX_TRAILING_ZEROS + 1]; + for (uint32_t i = 0; i < MAX_TRAILING_ZEROS; i++) { + uint32_t idx = MAX_TRAILING_ZEROS - i - 1; + uint32_t digit = (p_index / uint32_t(Math::pow(double(10), double(idx)))) % 10; + zeros[i] = '0' + digit; + } + zeros[MAX_TRAILING_ZEROS] = 0; + return zeros; +} + +Error MovieWriterPNGWAV::write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) { + // Quick & Dirty PNGWAV Code based on - https://docs.microsoft.com/en-us/windows/win32/directshow/avi-riff-file-reference + + base_path = p_base_path.get_basename(); + if (base_path.is_relative_path()) { + base_path = "res://" + base_path; + } + + { + //Remove existing files before writing anew + uint32_t idx = 0; + Ref<DirAccess> d = DirAccess::open(base_path.get_base_dir()); + String file = base_path.get_file(); + while (true) { + String path = file + zeros_str(idx) + ".png"; + if (d->remove(path) != OK) { + break; + } + } + } + + f_wav = FileAccess::open(base_path + ".wav", FileAccess::WRITE_READ); + ERR_FAIL_COND_V(f_wav.is_null(), ERR_CANT_OPEN); + + fps = p_fps; + + f_wav->store_buffer((const uint8_t *)"RIFF", 4); + int total_size = 4 /* WAVE */ + 8 /* fmt+size */ + 16 /* format */ + 8 /* data+size */; + f_wav->store_32(total_size); //will store final later + f_wav->store_buffer((const uint8_t *)"WAVE", 4); + + /* FORMAT CHUNK */ + + f_wav->store_buffer((const uint8_t *)"fmt ", 4); + + uint32_t channels = 2; + switch (speaker_mode) { + case AudioServer::SPEAKER_MODE_STEREO: + channels = 2; + break; + case AudioServer::SPEAKER_SURROUND_31: + channels = 4; + break; + case AudioServer::SPEAKER_SURROUND_51: + channels = 6; + break; + case AudioServer::SPEAKER_SURROUND_71: + channels = 8; + break; + } + + f_wav->store_32(16); //standard format, no extra fields + f_wav->store_16(1); // compression code, standard PCM + f_wav->store_16(channels); //CHANNELS: 2 + + f_wav->store_32(mix_rate); + + /* useless stuff the format asks for */ + + int bits_per_sample = 32; + int blockalign = bits_per_sample / 8 * channels; + int bytes_per_sec = mix_rate * blockalign; + + audio_block_size = (mix_rate / fps) * blockalign; + + f_wav->store_32(bytes_per_sec); + f_wav->store_16(blockalign); // block align (unused) + f_wav->store_16(bits_per_sample); + + /* DATA CHUNK */ + + f_wav->store_buffer((const uint8_t *)"data", 4); + + f_wav->store_32(0); //data size... wooh + wav_data_size_pos = f_wav->get_position(); + + return OK; +} + +Error MovieWriterPNGWAV::write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) { + ERR_FAIL_COND_V(!f_wav.is_valid(), ERR_UNCONFIGURED); + + Vector<uint8_t> png_buffer = p_image->save_png_to_buffer(); + + Ref<FileAccess> fi = FileAccess::open(base_path + zeros_str(frame_count) + ".png", FileAccess::WRITE); + fi->store_buffer(png_buffer.ptr(), png_buffer.size()); + f_wav->store_buffer((const uint8_t *)p_audio_data, audio_block_size); + + frame_count++; + + return OK; +} + +void MovieWriterPNGWAV::write_end() { + if (f_wav.is_valid()) { + uint32_t total_size = 4 /* WAVE */ + 8 /* fmt+size */ + 16 /* format */ + 8 /* data+size */; + uint32_t datasize = f_wav->get_position() - wav_data_size_pos; + f_wav->seek(4); + f_wav->store_32(total_size + datasize); + f_wav->seek(0x28); + f_wav->store_32(datasize); + } +} + +MovieWriterPNGWAV::MovieWriterPNGWAV() { + mix_rate = GLOBAL_GET("editor/movie_writer/mix_rate"); + speaker_mode = AudioServer::SpeakerMode(int(GLOBAL_GET("editor/movie_writer/speaker_mode"))); +} diff --git a/servers/movie_writer/movie_writer_pngwav.h b/servers/movie_writer/movie_writer_pngwav.h new file mode 100644 index 0000000000..64608ce387 --- /dev/null +++ b/servers/movie_writer/movie_writer_pngwav.h @@ -0,0 +1,71 @@ +/*************************************************************************/ +/* movie_writer_pngwav.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef MOVIE_WRITER_PNGWAV_H +#define MOVIE_WRITER_PNGWAV_H + +#include "servers/movie_writer/movie_writer.h" + +class MovieWriterPNGWAV : public MovieWriter { + GDCLASS(MovieWriterPNGWAV, MovieWriter) + + enum { + MAX_TRAILING_ZEROS = 8 // more than 10 days at 60fps, no hard drive can put up with this anyway :) + }; + + uint32_t mix_rate = 48000; + AudioServer::SpeakerMode speaker_mode = AudioServer::SPEAKER_MODE_STEREO; + String base_path; + uint32_t frame_count = 0; + uint32_t fps = 0; + + uint32_t audio_block_size = 0; + + Ref<FileAccess> f_wav; + uint32_t wav_data_size_pos = 0; + + String zeros_str(uint32_t p_index); + +protected: + virtual uint32_t get_audio_mix_rate() const override; + virtual AudioServer::SpeakerMode get_audio_speaker_mode() const override; + virtual void get_supported_extensions(List<String> *r_extensions) const override; + + virtual Error write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) override; + virtual Error write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) override; + virtual void write_end() override; + + virtual bool handles_file(const String &p_path) const override; + +public: + MovieWriterPNGWAV(); +}; + +#endif // MOVIE_WRITER_PNGWAV_H diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index d7d2340119..db473f6296 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -59,6 +59,7 @@ #include "display_server.h" #include "movie_writer/movie_writer.h" #include "movie_writer/movie_writer_mjpeg.h" +#include "movie_writer/movie_writer_pngwav.h" #include "navigation_server_2d.h" #include "navigation_server_3d.h" #include "physics_2d/godot_physics_server_2d.h" |