diff options
58 files changed, 1998 insertions, 487 deletions
diff --git a/.travis.yml b/.travis.yml index 409c870e79..974ef93d3f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,12 +20,12 @@ matrix: #- env: GODOT_TARGET=windows TOOLS=yes # os: linux # compiler: gcc + - env: GODOT_TARGET=android TOOLS=no + os: linux + compiler: gcc - env: GODOT_TARGET=osx TOOLS=yes os: osx compiler: clang - #- env: GODOT_TARGET=android TOOLS=no - # os: osx - # compiler: clang #- env: GODOT_TARGET=iphone TOOLS=no # os: osx # compiler: clang @@ -61,15 +61,29 @@ addons: # For style checks. - clang-format-3.9 +install: + - if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$GODOT_TARGET" = "android" ]; then + misc/travis/android-tools-linux.sh; + fi + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then + misc/travis/scons-local-osx.sh; + misc/travis/ccache-osx.sh; + fi + - if [ "$TRAVIS_OS_NAME" = "osx" ] && [ "$GODOT_TARGET" = "android" ]; then + misc/travis/android-tools-osx.sh; + fi + before_script: + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then + export CCACHE="/usr/bin/ccache"; + fi - if [ "$TRAVIS_OS_NAME" = "osx" ]; then - brew update; brew install scons ccache; + export CCACHE="/usr/local/bin/ccache"; export PATH="/usr/local/opt/ccache/libexec:$PATH"; fi - - if [ "$TRAVIS_OS_NAME" = "osx" ] && [ "$GODOT_TARGET" = "android" ]; then - brew update; brew install -v android-sdk; - brew install -v android-ndk | grep -v "inflating:" | grep -v "creating:"; - export ANDROID_HOME=/usr/local/opt/android-sdk; export ANDROID_NDK_ROOT=/usr/local/opt/android-ndk; + - if [ "$GODOT_TARGET" = "android" ]; then + export ANDROID_HOME=$TRAVIS_BUILD_DIR/godot-dev/build-tools/android-sdk; + export ANDROID_NDK_ROOT=$TRAVIS_BUILD_DIR/godot-dev/build-tools/android-ndk; fi script: @@ -78,3 +92,10 @@ script: else scons -j2 CC=$CC CXX=$CXX platform=$GODOT_TARGET TOOLS=$TOOLS verbose=yes progress=no; fi + +after_success: + - if [ "$STATIC_CHECKS" != "yes" ] && [ $(command -v ccache) ]; then + which ccache; + ccache --version | grep "ccache version"; + ccache -s; + fi; diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp index df4254e2a3..84d55b4d14 100644 --- a/editor/import_dock.cpp +++ b/editor/import_dock.cpp @@ -87,23 +87,7 @@ void ImportDock::set_edit_path(const String &p_path) { return; } - List<ResourceImporter::ImportOption> options; - params->importer->get_import_options(&options); - - params->properties.clear(); - params->values.clear(); - - for (List<ResourceImporter::ImportOption>::Element *E = options.front(); E; E = E->next()) { - - params->properties.push_back(E->get().option); - if (config->has_section_key("params", E->get().option.name)) { - params->values[E->get().option.name] = config->get_value("params", E->get().option.name); - } else { - params->values[E->get().option.name] = E->get().default_value; - } - } - - params->update(); + _update_options(config); List<Ref<ResourceImporter> > importers; ResourceFormatImporter::get_singleton()->get_importers_for_extension(p_path.get_extension(), &importers); @@ -125,6 +109,34 @@ void ImportDock::set_edit_path(const String &p_path) { } } + params->paths.clear(); + params->paths.push_back(p_path); + import->set_disabled(false); + import_as->set_disabled(false); + + imported->set_text(p_path.get_file()); +} + +void ImportDock::_update_options(const Ref<ConfigFile> &p_config) { + + List<ResourceImporter::ImportOption> options; + params->importer->get_import_options(&options); + + params->properties.clear(); + params->values.clear(); + + for (List<ResourceImporter::ImportOption>::Element *E = options.front(); E; E = E->next()) { + + params->properties.push_back(E->get().option); + if (p_config.is_valid() && p_config->has_section_key("params", E->get().option.name)) { + params->values[E->get().option.name] = p_config->get_value("params", E->get().option.name); + } else { + params->values[E->get().option.name] = E->get().default_value; + } + } + + params->update(); + preset->get_popup()->clear(); if (params->importer->get_preset_count() == 0) { @@ -142,13 +154,6 @@ void ImportDock::set_edit_path(const String &p_path) { preset->get_popup()->add_separator(); preset->get_popup()->add_item(vformat(TTR("Clear Default for '%s'"), params->importer->get_visible_name()), ITEM_CLEAR_DEFAULT); } - - params->paths.clear(); - params->paths.push_back(p_path); - import->set_disabled(false); - import_as->set_disabled(false); - - imported->set_text(p_path.get_file()); } void ImportDock::set_edit_multiple_paths(const Vector<String> &p_paths) { @@ -269,6 +274,16 @@ void ImportDock::_importer_selected(int i_idx) { ERR_FAIL_COND(importer.is_null()); params->importer = importer; + + Ref<ConfigFile> config; + if (params->paths.size()) { + config.instance(); + Error err = config->load(params->paths[0] + ".import"); + if (err != OK) { + config.unref(); + } + } + _update_options(config); } void ImportDock::_preset_selected(int p_idx) { diff --git a/editor/import_dock.h b/editor/import_dock.h index a9bb22e568..28c29e4b20 100644 --- a/editor/import_dock.h +++ b/editor/import_dock.h @@ -55,6 +55,7 @@ class ImportDock : public VBoxContainer { void _preset_selected(int p_idx); void _importer_selected(int i_idx); + void _update_options(const Ref<ConfigFile> &p_config = Ref<ConfigFile>()); void _reimport(); diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index f2f913d2b3..736e176ab8 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -563,6 +563,14 @@ void AbstractPolygon2DEditor::forward_draw_over_canvas(Control *p_canvas) { const Vector2 next_point = xform.xform(p2); vpc->draw_line(point, next_point, col, 2); } + } + + for (int i = 0; i < n_points; i++) { + + const Vertex vertex(j, i); + + const Vector2 p = (vertex == edited_point) ? edited_point.pos : (points[i] + offset); + const Vector2 point = xform.xform(p); Ref<Texture> handle = vertex == active_point ? selected_handle : default_handle; vpc->draw_texture(handle, point - handle->get_size() * 0.5); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index a1183307fb..607ccaa4e7 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -865,20 +865,7 @@ void ScriptEditor::_menu_option(int p_option) { } break; case SEARCH_CLASSES: { - String current; - - if (tab_container->get_tab_count() > 0) { - EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_child(tab_container->get_current_tab())); - if (eh) { - current = eh->get_class(); - } - } - help_index->popup(); - - if (current != "") { - help_index->call_deferred("select_class", current); - } } break; case SEARCH_WEBSITE: { @@ -890,8 +877,13 @@ void ScriptEditor::_menu_option(int p_option) { _history_forward(); } break; case WINDOW_PREV: { + _history_back(); } break; + case WINDOW_SORT: { + _sort_list_on_update = true; + _update_script_names(); + } break; case DEBUG_SHOW: { if (debugger) { bool visible = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_SHOW)); @@ -926,10 +918,6 @@ void ScriptEditor::_menu_option(int p_option) { if (current) { switch (p_option) { - case FILE_NEW: { - script_create_dialog->config("Node", ".gd"); - script_create_dialog->popup_centered(Size2(300, 300) * EDSCALE); - } break; case FILE_SAVE: { if (_test_script_times_on_disk()) @@ -1041,26 +1029,22 @@ void ScriptEditor::_menu_option(int p_option) { debugger->debug_continue(); } break; - case WINDOW_MOVE_LEFT: { + case WINDOW_MOVE_UP: { if (tab_container->get_current_tab() > 0) { - tab_container->call_deferred("set_current_tab", tab_container->get_current_tab() - 1); - script_list->call_deferred("select", tab_container->get_current_tab() - 1); tab_container->move_child(current, tab_container->get_current_tab() - 1); + tab_container->set_current_tab(tab_container->get_current_tab() - 1); _update_script_names(); } } break; - case WINDOW_MOVE_RIGHT: { + case WINDOW_MOVE_DOWN: { if (tab_container->get_current_tab() < tab_container->get_child_count() - 1) { - tab_container->call_deferred("set_current_tab", tab_container->get_current_tab() + 1); - script_list->call_deferred("select", tab_container->get_current_tab() + 1); tab_container->move_child(current, tab_container->get_current_tab() + 1); + tab_container->set_current_tab(tab_container->get_current_tab() + 1); _update_script_names(); } - } break; - default: { if (p_option >= WINDOW_SELECT_BASE) { @@ -1077,6 +1061,11 @@ void ScriptEditor::_menu_option(int p_option) { switch (p_option) { + case SEARCH_CLASSES: { + + help_index->popup(); + help_index->call_deferred("select_class", help->get_class()); + } break; case HELP_SEARCH_FIND: { help->popup_search(); } break; @@ -1092,6 +1081,22 @@ void ScriptEditor::_menu_option(int p_option) { case CLOSE_ALL: { _close_all_tabs(); } break; + case WINDOW_MOVE_UP: { + + if (tab_container->get_current_tab() > 0) { + tab_container->move_child(help, tab_container->get_current_tab() - 1); + tab_container->set_current_tab(tab_container->get_current_tab() - 1); + _update_script_names(); + } + } break; + case WINDOW_MOVE_DOWN: { + + if (tab_container->get_current_tab() < tab_container->get_child_count() - 1) { + tab_container->move_child(help, tab_container->get_current_tab() + 1); + tab_container->set_current_tab(tab_container->get_current_tab() + 1); + _update_script_names(); + } + } break; } } } @@ -1361,6 +1366,7 @@ struct _ScriptEditorItemData { String tooltip; bool used; int category; + Node *ref; bool operator<(const _ScriptEditorItemData &id) const { @@ -1526,6 +1532,7 @@ void ScriptEditor::_update_script_names() { sd.index = i; sd.used = used.has(se->get_edited_script()); sd.category = 0; + sd.ref = se; switch (sort_by) { case SORT_BY_NAME: { @@ -1565,16 +1572,38 @@ void ScriptEditor::_update_script_names() { _ScriptEditorItemData sd; sd.icon = icon; sd.name = name; - sd.sort_key = name; + sd.sort_key = name.to_lower(); sd.tooltip = tooltip; sd.index = i; sd.used = false; sd.category = split_script_help ? 1 : 0; + sd.ref = eh; + sedata.push_back(sd); } } - sedata.sort(); + if (_sort_list_on_update) { + sedata.sort(); + + // change actual order of tab_container so that the order can be rearranged by user + int cur_tab = tab_container->get_current_tab(); + int prev_tab = tab_container->get_previous_tab(); + int new_cur_tab = -1; + int new_prev_tab = -1; + for (int i = 0; i < sedata.size(); i++) { + tab_container->move_child(sedata[i].ref, i); + if (new_prev_tab == -1 && sedata[i].index == prev_tab) { + new_prev_tab = i; + } + if (new_cur_tab == -1 && sedata[i].index == cur_tab) { + new_cur_tab = i; + } + } + tab_container->call_deferred("set_current_tab", new_prev_tab); + tab_container->call_deferred("set_current_tab", new_cur_tab); + _sort_list_on_update = false; + } for (int i = 0; i < sedata.size(); i++) { @@ -1903,8 +1932,171 @@ void ScriptEditor::_script_split_dragged(float) { _save_layout(); } +Variant ScriptEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) { + + // return Variant(); // return this if drag disabled + + Node *cur_node = tab_container->get_child(tab_container->get_current_tab()); + + HBoxContainer *drag_preview = memnew(HBoxContainer); + String preview_name = ""; + Ref<Texture> preview_icon; + + ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(cur_node); + if (se) { + preview_name = se->get_name(); + preview_icon = se->get_icon(); + } + EditorHelp *eh = Object::cast_to<EditorHelp>(cur_node); + if (eh) { + preview_name = eh->get_class(); + preview_icon = get_icon("Help", "EditorIcons"); + } + + if (!preview_icon.is_null()) { + TextureRect *tf = memnew(TextureRect); + tf->set_texture(preview_icon); + drag_preview->add_child(tf); + } + Label *label = memnew(Label(preview_name)); + drag_preview->add_child(label); + set_drag_preview(drag_preview); + + Dictionary drag_data; + drag_data["type"] = "script_list_element"; // using a custom type because node caused problems when dragging to scene tree + drag_data["script_list_element"] = cur_node; + + return drag_data; +} + +bool ScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { + + Dictionary d = p_data; + if (!d.has("type")) + return false; + + if (String(d["type"]) == "script_list_element") { + + Node *node = d["script_list_element"]; + + ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(node); + if (se) { + return true; + } + EditorHelp *eh = Object::cast_to<EditorHelp>(node); + if (eh) { + return true; + } + } + + if (String(d["type"]) == "nodes") { + + Array nodes = d["nodes"]; + if (nodes.size() == 0) + return false; + Node *node = get_node((nodes[0])); + + ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(node); + if (se) { + return true; + } + EditorHelp *eh = Object::cast_to<EditorHelp>(node); + if (eh) { + return true; + } + } + + if (String(d["type"]) == "files") { + + Vector<String> files = d["files"]; + + if (files.size() == 0) + return false; //weird + + for (int i = 0; i < files.size(); i++) { + String file = files[i]; + if (file == "" || !FileAccess::exists(file)) + continue; + Ref<Script> scr = ResourceLoader::load(file); + if (scr.is_valid()) { + return true; + } + } + return true; + } + + return false; +} + +void ScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { + + if (!can_drop_data_fw(p_point, p_data, p_from)) + return; + + Dictionary d = p_data; + if (!d.has("type")) + return; + + if (String(d["type"]) == "script_list_element") { + + Node *node = d["script_list_element"]; + + ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(node); + EditorHelp *eh = Object::cast_to<EditorHelp>(node); + if (se || eh) { + int new_index = script_list->get_item_at_position(p_point); + tab_container->move_child(node, new_index); + tab_container->set_current_tab(new_index); + _update_script_names(); + } + } + + if (String(d["type"]) == "nodes") { + + Array nodes = d["nodes"]; + if (nodes.size() == 0) + return; + Node *node = get_node(nodes[0]); + + ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(node); + EditorHelp *eh = Object::cast_to<EditorHelp>(node); + if (se || eh) { + int new_index = script_list->get_item_at_position(p_point); + tab_container->move_child(node, new_index); + tab_container->set_current_tab(new_index); + _update_script_names(); + } + } + + if (String(d["type"]) == "files") { + + Vector<String> files = d["files"]; + + int new_index = script_list->get_item_at_position(p_point); + int num_tabs_before = tab_container->get_child_count(); + for (int i = 0; i < files.size(); i++) { + String file = files[i]; + if (file == "" || !FileAccess::exists(file)) + continue; + Ref<Script> scr = ResourceLoader::load(file); + if (scr.is_valid()) { + edit(scr); + if (tab_container->get_child_count() > num_tabs_before) { + tab_container->move_child(tab_container->get_child(tab_container->get_child_count() - 1), new_index); + num_tabs_before = tab_container->get_child_count(); + } else { + tab_container->move_child(tab_container->get_child(tab_container->get_current_tab()), new_index); + } + } + } + tab_container->set_current_tab(new_index); + _update_script_names(); + } +} + void ScriptEditor::_unhandled_input(const Ref<InputEvent> &p_event) { - if (p_event->is_pressed() || !is_visible_in_tree()) return; + if (!is_visible_in_tree() || !p_event->is_pressed() || p_event->is_echo()) + return; if (ED_IS_SHORTCUT("script_editor/next_script", p_event)) { int next_tab = script_list->get_current() + 1; next_tab %= script_list->get_item_count(); @@ -1917,6 +2109,62 @@ void ScriptEditor::_unhandled_input(const Ref<InputEvent> &p_event) { _go_to_tab(script_list->get_item_metadata(next_tab)); _update_script_names(); } + if (ED_IS_SHORTCUT("script_editor/window_move_up", p_event)) { + _menu_option(WINDOW_MOVE_UP); + } + if (ED_IS_SHORTCUT("script_editor/window_move_down", p_event)) { + _menu_option(WINDOW_MOVE_DOWN); + } +} + +void ScriptEditor::_script_list_gui_input(const Ref<InputEvent> &ev) { + + Ref<InputEventMouseButton> mb = ev; + if (mb.is_valid() && mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed()) { + + _make_script_list_context_menu(); + } +} + +void ScriptEditor::_make_script_list_context_menu() { + + context_menu->clear(); + + int selected = tab_container->get_current_tab(); + if (selected < 0 || selected >= tab_container->get_child_count()) + return; + + ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected)); + if (se) { + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/save"), FILE_SAVE); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/save_as"), FILE_SAVE_AS); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_file"), FILE_CLOSE); + context_menu->add_separator(); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/reload_script_soft"), FILE_TOOL_RELOAD_SOFT); + + Ref<Script> scr = se->get_edited_script(); + if (!scr.is_null() && scr->is_tool()) { + context_menu->add_separator(); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/run_file"), FILE_RUN); + } + } else { + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_file"), FILE_CLOSE); + } + + EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_child(selected)); + if (eh) { + // nothing + } + + context_menu->add_separator(); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/window_move_up"), WINDOW_MOVE_UP); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/window_move_down"), WINDOW_MOVE_DOWN); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/window_sort"), WINDOW_SORT); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/toggle_scripts_panel"), TOGGLE_SCRIPTS_PANEL); + + context_menu->set_position(get_global_transform().xform(get_local_mouse_position())); + context_menu->set_size(Vector2(1, 1)); + context_menu->popup(); } void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) { @@ -2243,9 +2491,14 @@ void ScriptEditor::_bind_methods() { ClassDB::bind_method("_history_back", &ScriptEditor::_history_back); ClassDB::bind_method("_live_auto_reload_running_scripts", &ScriptEditor::_live_auto_reload_running_scripts); ClassDB::bind_method("_unhandled_input", &ScriptEditor::_unhandled_input); + ClassDB::bind_method("_script_list_gui_input", &ScriptEditor::_script_list_gui_input); ClassDB::bind_method("_script_changed", &ScriptEditor::_script_changed); ClassDB::bind_method("_update_recent_scripts", &ScriptEditor::_update_recent_scripts); + ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &ScriptEditor::get_drag_data_fw); + ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &ScriptEditor::can_drop_data_fw); + ClassDB::bind_method(D_METHOD("drop_data_fw"), &ScriptEditor::drop_data_fw); + ClassDB::bind_method(D_METHOD("get_current_script"), &ScriptEditor::_get_current_script); ClassDB::bind_method(D_METHOD("get_open_scripts"), &ScriptEditor::_get_open_scripts); @@ -2287,6 +2540,15 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { script_split->set_split_offset(140); //list_split->set_split_offset(500); + _sort_list_on_update = true; + script_list->connect("gui_input", this, "_script_list_gui_input"); + script_list->set_allow_rmb_select(true); + script_list->set_drag_forwarding(this); + + context_menu = memnew(PopupMenu); + add_child(context_menu); + context_menu->connect("id_pressed", this, "_menu_option"); + members_overview = memnew(ItemList); list_split->add_child(members_overview); members_overview->set_custom_minimum_size(Size2(0, 100)); //need to give a bit of limit to avoid it from disappearing @@ -2303,8 +2565,11 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { tab_container->set_h_size_flags(SIZE_EXPAND_FILL); - ED_SHORTCUT("script_editor/next_script", TTR("Next script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_GREATER); - ED_SHORTCUT("script_editor/prev_script", TTR("Previous script"), KEY_MASK_CMD | KEY_LESS); + ED_SHORTCUT("script_editor/window_sort", TTR("Sort")); + ED_SHORTCUT("script_editor/window_move_up", TTR("Move Up"), KEY_MASK_SHIFT | KEY_MASK_ALT | KEY_UP); + ED_SHORTCUT("script_editor/window_move_down", TTR("Move Down"), KEY_MASK_SHIFT | KEY_MASK_ALT | KEY_DOWN); + ED_SHORTCUT("script_editor/next_script", TTR("Next script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_PERIOD); // these should be KEY_GREATER and KEY_LESS but those don't work + ED_SHORTCUT("script_editor/prev_script", TTR("Previous script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_COLON); set_process_unhandled_input(true); file_menu = memnew(MenuButton); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 03fc4da7ce..b8317f9e86 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -150,10 +150,11 @@ class ScriptEditor : public PanelContainer { SEARCH_WEBSITE, HELP_SEARCH_FIND, HELP_SEARCH_FIND_NEXT, - WINDOW_MOVE_LEFT, - WINDOW_MOVE_RIGHT, + WINDOW_MOVE_UP, + WINDOW_MOVE_DOWN, WINDOW_NEXT, WINDOW_PREV, + WINDOW_SORT, WINDOW_SELECT_BASE = 100 }; @@ -173,6 +174,7 @@ class ScriptEditor : public PanelContainer { MenuButton *edit_menu; MenuButton *script_search_menu; MenuButton *debug_menu; + PopupMenu *context_menu; Timer *autosave_timer; uint64_t idle; @@ -292,6 +294,7 @@ class ScriptEditor : public PanelContainer { void _update_members_overview_visibility(); void _update_members_overview(); void _update_script_names(); + bool _sort_list_on_update; void _members_overview_selected(int p_idx); void _script_selected(int p_idx); @@ -306,8 +309,15 @@ class ScriptEditor : public PanelContainer { void _script_split_dragged(float); + Variant get_drag_data_fw(const Point2 &p_point, Control *p_from); + bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; + void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); + void _unhandled_input(const Ref<InputEvent> &p_event); + void _script_list_gui_input(const Ref<InputEvent> &ev); + void _make_script_list_context_menu(); + void _help_search(String p_text); void _help_index(String p_text); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index adf65c11e1..6b945157e8 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -850,7 +850,8 @@ void ScriptTextEditor::_edit_option(int p_op) { if (line_id == 0 || next_id < 0) return; - swap_lines(tx, line_id, next_id); + tx->swap_lines(line_id, next_id); + tx->cursor_set_line(next_id); } int from_line_up = from_line > 0 ? from_line - 1 : from_line; int to_line_up = to_line > 0 ? to_line - 1 : to_line; @@ -862,7 +863,8 @@ void ScriptTextEditor::_edit_option(int p_op) { if (line_id == 0 || next_id < 0) return; - swap_lines(tx, line_id, next_id); + tx->swap_lines(line_id, next_id); + tx->cursor_set_line(next_id); } tx->end_complex_operation(); tx->update(); @@ -889,7 +891,8 @@ void ScriptTextEditor::_edit_option(int p_op) { if (line_id == tx->get_line_count() - 1 || next_id > tx->get_line_count()) return; - swap_lines(tx, line_id, next_id); + tx->swap_lines(line_id, next_id); + tx->cursor_set_line(next_id); } int from_line_down = from_line < tx->get_line_count() ? from_line + 1 : from_line; int to_line_down = to_line < tx->get_line_count() ? to_line + 1 : to_line; @@ -901,7 +904,8 @@ void ScriptTextEditor::_edit_option(int p_op) { if (line_id == tx->get_line_count() - 1 || next_id > tx->get_line_count()) return; - swap_lines(tx, line_id, next_id); + tx->swap_lines(line_id, next_id); + tx->cursor_set_line(next_id); } tx->end_complex_operation(); tx->update(); diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index f7dcc4b52d..49e4642049 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -248,6 +248,8 @@ void ShaderTextEditor::_validate_script() { if (err != OK) { String error_text = "error(" + itos(sl.get_error_line()) + "): " + sl.get_error_text(); set_error(error_text); + for (int i = 0; i < get_text_edit()->get_line_count(); i++) + get_text_edit()->set_line_as_marked(i, false); get_text_edit()->set_line_as_marked(sl.get_error_line() - 1, true); } else { @@ -269,55 +271,284 @@ ShaderTextEditor::ShaderTextEditor() { void ShaderEditor::_menu_option(int p_option) { - ShaderTextEditor *current = shader_editor; - switch (p_option) { case EDIT_UNDO: { - - current->get_text_edit()->undo(); + shader_editor->get_text_edit()->undo(); } break; case EDIT_REDO: { - current->get_text_edit()->redo(); - + shader_editor->get_text_edit()->redo(); } break; case EDIT_CUT: { - - current->get_text_edit()->cut(); + shader_editor->get_text_edit()->cut(); } break; case EDIT_COPY: { - current->get_text_edit()->copy(); - + shader_editor->get_text_edit()->copy(); } break; case EDIT_PASTE: { - current->get_text_edit()->paste(); - + shader_editor->get_text_edit()->paste(); } break; case EDIT_SELECT_ALL: { + shader_editor->get_text_edit()->select_all(); + } break; + case EDIT_MOVE_LINE_UP: { + + TextEdit *tx = shader_editor->get_text_edit(); + if (shader.is_null()) + return; + + tx->begin_complex_operation(); + if (tx->is_selection_active()) { + int from_line = tx->get_selection_from_line(); + int from_col = tx->get_selection_from_column(); + int to_line = tx->get_selection_to_line(); + int to_column = tx->get_selection_to_column(); + + for (int i = from_line; i <= to_line; i++) { + int line_id = i; + int next_id = i - 1; + + if (line_id == 0 || next_id < 0) + return; + + tx->swap_lines(line_id, next_id); + tx->cursor_set_line(next_id); + } + int from_line_up = from_line > 0 ? from_line - 1 : from_line; + int to_line_up = to_line > 0 ? to_line - 1 : to_line; + tx->select(from_line_up, from_col, to_line_up, to_column); + } else { + int line_id = tx->cursor_get_line(); + int next_id = line_id - 1; + + if (line_id == 0 || next_id < 0) + return; + + tx->swap_lines(line_id, next_id); + tx->cursor_set_line(next_id); + } + tx->end_complex_operation(); + tx->update(); + + } break; + case EDIT_MOVE_LINE_DOWN: { + + TextEdit *tx = shader_editor->get_text_edit(); + if (shader.is_null()) + return; + + tx->begin_complex_operation(); + if (tx->is_selection_active()) { + int from_line = tx->get_selection_from_line(); + int from_col = tx->get_selection_from_column(); + int to_line = tx->get_selection_to_line(); + int to_column = tx->get_selection_to_column(); + + for (int i = to_line; i >= from_line; i--) { + int line_id = i; + int next_id = i + 1; + + if (line_id == tx->get_line_count() - 1 || next_id > tx->get_line_count()) + return; + + tx->swap_lines(line_id, next_id); + tx->cursor_set_line(next_id); + } + int from_line_down = from_line < tx->get_line_count() ? from_line + 1 : from_line; + int to_line_down = to_line < tx->get_line_count() ? to_line + 1 : to_line; + tx->select(from_line_down, from_col, to_line_down, to_column); + } else { + int line_id = tx->cursor_get_line(); + int next_id = line_id + 1; + + if (line_id == tx->get_line_count() - 1 || next_id > tx->get_line_count()) + return; + + tx->swap_lines(line_id, next_id); + tx->cursor_set_line(next_id); + } + tx->end_complex_operation(); + tx->update(); + + } break; + case EDIT_INDENT_LEFT: { + + TextEdit *tx = shader_editor->get_text_edit(); + if (shader.is_null()) + return; + + tx->begin_complex_operation(); + if (tx->is_selection_active()) { + tx->indent_selection_left(); + } else { + int begin = tx->cursor_get_line(); + String line_text = tx->get_line(begin); + // begins with tab + if (line_text.begins_with("\t")) { + line_text = line_text.substr(1, line_text.length()); + tx->set_line(begin, line_text); + } + // begins with 4 spaces + else if (line_text.begins_with(" ")) { + line_text = line_text.substr(4, line_text.length()); + tx->set_line(begin, line_text); + } + } + tx->end_complex_operation(); + tx->update(); + //tx->deselect(); + + } break; + case EDIT_INDENT_RIGHT: { + + TextEdit *tx = shader_editor->get_text_edit(); + if (shader.is_null()) + return; + + tx->begin_complex_operation(); + if (tx->is_selection_active()) { + tx->indent_selection_right(); + } else { + int begin = tx->cursor_get_line(); + String line_text = tx->get_line(begin); + line_text = '\t' + line_text; + tx->set_line(begin, line_text); + } + tx->end_complex_operation(); + tx->update(); + //tx->deselect(); + + } break; + case EDIT_DELETE_LINE: { - current->get_text_edit()->select_all(); + TextEdit *tx = shader_editor->get_text_edit(); + if (shader.is_null()) + return; + + tx->begin_complex_operation(); + int line = tx->cursor_get_line(); + tx->set_line(tx->cursor_get_line(), ""); + tx->backspace_at_cursor(); + tx->cursor_set_line(line); + tx->end_complex_operation(); } break; + case EDIT_CLONE_DOWN: { + + TextEdit *tx = shader_editor->get_text_edit(); + if (shader.is_null()) + return; + + int from_line = tx->cursor_get_line(); + int to_line = tx->cursor_get_line(); + int column = tx->cursor_get_column(); + + if (tx->is_selection_active()) { + from_line = tx->get_selection_from_line(); + to_line = tx->get_selection_to_line(); + column = tx->cursor_get_column(); + } + int next_line = to_line + 1; + + tx->begin_complex_operation(); + for (int i = from_line; i <= to_line; i++) { + + if (i >= tx->get_line_count() - 1) { + tx->set_line(i, tx->get_line(i) + "\n"); + } + String line_clone = tx->get_line(i); + tx->insert_at(line_clone, next_line); + next_line++; + } + + tx->cursor_set_column(column); + if (tx->is_selection_active()) { + tx->select(to_line + 1, tx->get_selection_from_column(), next_line - 1, tx->get_selection_to_column()); + } + + tx->end_complex_operation(); + tx->update(); + + } break; + case EDIT_TOGGLE_COMMENT: { + + TextEdit *tx = shader_editor->get_text_edit(); + if (shader.is_null()) + return; + + tx->begin_complex_operation(); + if (tx->is_selection_active()) { + int begin = tx->get_selection_from_line(); + int end = tx->get_selection_to_line(); + + // End of selection ends on the first column of the last line, ignore it. + if (tx->get_selection_to_column() == 0) + end -= 1; + + // Check if all lines in the selected block are commented + bool is_commented = true; + for (int i = begin; i <= end; i++) { + if (!tx->get_line(i).begins_with("//")) { + is_commented = false; + break; + } + } + for (int i = begin; i <= end; i++) { + String line_text = tx->get_line(i); + + if (line_text.strip_edges().empty()) { + line_text = "//"; + } else { + if (is_commented) { + line_text = line_text.substr(2, line_text.length()); + } else { + line_text = "//" + line_text; + } + } + tx->set_line(i, line_text); + } + } else { + int begin = tx->cursor_get_line(); + String line_text = tx->get_line(begin); + + if (line_text.begins_with("//")) + line_text = line_text.substr(2, line_text.length()); + else + line_text = "//" + line_text; + tx->set_line(begin, line_text); + } + tx->end_complex_operation(); + tx->update(); + //tx->deselect(); + + } break; + case EDIT_COMPLETE: { + + shader_editor->get_text_edit()->query_code_comple(); + } break; case SEARCH_FIND: { - current->get_find_replace_bar()->popup_search(); + shader_editor->get_find_replace_bar()->popup_search(); } break; case SEARCH_FIND_NEXT: { - current->get_find_replace_bar()->search_next(); + shader_editor->get_find_replace_bar()->search_next(); } break; case SEARCH_FIND_PREV: { - current->get_find_replace_bar()->search_prev(); + shader_editor->get_find_replace_bar()->search_prev(); } break; case SEARCH_REPLACE: { - current->get_find_replace_bar()->popup_replace(); + shader_editor->get_find_replace_bar()->popup_replace(); } break; case SEARCH_GOTO_LINE: { - goto_line_dialog->popup_find_line(current->get_text_edit()); + goto_line_dialog->popup_find_line(shader_editor->get_text_edit()); } break; } + if (p_option != SEARCH_FIND && p_option != SEARCH_REPLACE && p_option != SEARCH_GOTO_LINE) { + shader_editor->get_text_edit()->call_deferred("grab_focus"); + } } void ShaderEditor::_notification(int p_what) { @@ -325,10 +556,6 @@ void ShaderEditor::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { } if (p_what == NOTIFICATION_DRAW) { - - RID ci = get_canvas_item(); - Ref<StyleBox> style = get_stylebox("panel", "Panel"); - style->draw(ci, Rect2(Point2(), get_size())); } } @@ -360,6 +587,7 @@ void ShaderEditor::_editor_settings_changed() { void ShaderEditor::_bind_methods() { ClassDB::bind_method("_editor_settings_changed", &ShaderEditor::_editor_settings_changed); + ClassDB::bind_method("_text_edit_gui_input", &ShaderEditor::_text_edit_gui_input); ClassDB::bind_method("_menu_option", &ShaderEditor::_menu_option); ClassDB::bind_method("_params_changed", &ShaderEditor::_params_changed); @@ -413,49 +641,122 @@ void ShaderEditor::apply_shaders() { } } -ShaderEditor::ShaderEditor() { +void ShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { - HBoxContainer *hbc = memnew(HBoxContainer); + Ref<InputEventMouseButton> mb = ev; - add_child(hbc); + if (mb.is_valid()) { + + if (mb->get_button_index() == BUTTON_RIGHT && !mb->is_pressed()) { + + int col, row; + TextEdit *tx = shader_editor->get_text_edit(); + tx->_get_mouse_pos(mb->get_global_position() - tx->get_global_position(), row, col); + Vector2 mpos = mb->get_global_position() - tx->get_global_position(); + bool have_selection = (tx->get_selection_text().length() > 0); + _make_context_menu(have_selection); + } + } +} + +void ShaderEditor::_make_context_menu(bool p_selection) { + + context_menu->clear(); + if (p_selection) { + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/cut"), EDIT_CUT); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/copy"), EDIT_COPY); + } + + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/paste"), EDIT_PASTE); + context_menu->add_separator(); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/select_all"), EDIT_SELECT_ALL); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); + + context_menu->add_separator(); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_left"), EDIT_INDENT_LEFT); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_right"), EDIT_INDENT_RIGHT); + context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT); + + context_menu->set_position(get_global_transform().xform(get_local_mouse_position())); + context_menu->set_size(Vector2(1, 1)); + context_menu->popup(); +} + +ShaderEditor::ShaderEditor(EditorNode *p_node) { + + shader_editor = memnew(ShaderTextEditor); + shader_editor->set_v_size_flags(SIZE_EXPAND_FILL); + shader_editor->add_constant_override("separation", 0); + shader_editor->set_anchors_and_margins_preset(Control::PRESET_WIDE); + + shader_editor->connect("script_changed", this, "apply_shaders"); + EditorSettings::get_singleton()->connect("settings_changed", this, "_editor_settings_changed"); + + shader_editor->get_text_edit()->set_callhint_settings( + EditorSettings::get_singleton()->get("text_editor/completion/put_callhint_tooltip_below_current_line"), + EditorSettings::get_singleton()->get("text_editor/completion/callhint_tooltip_offset")); + + shader_editor->get_text_edit()->set_select_identifiers_on_hover(true); + shader_editor->get_text_edit()->set_context_menu_enabled(false); + shader_editor->get_text_edit()->connect("gui_input", this, "_text_edit_gui_input"); + + shader_editor->update_editor_settings(); + + context_menu = memnew(PopupMenu); + add_child(context_menu); + context_menu->connect("id_pressed", this, "_menu_option"); + + VBoxContainer *main_container = memnew(VBoxContainer); + HBoxContainer *hbc = memnew(HBoxContainer); edit_menu = memnew(MenuButton); - hbc->add_child(edit_menu); - edit_menu->set_position(Point2(5, -1)); + //edit_menu->set_position(Point2(5, -1)); edit_menu->set_text(TTR("Edit")); - edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/undo", TTR("Undo"), KEY_MASK_CMD | KEY_Z), EDIT_UNDO); - edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/redo", TTR("Redo"), KEY_MASK_CMD | KEY_Y), EDIT_REDO); + + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO); + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/cut"), EDIT_CUT); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/copy"), EDIT_COPY); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/paste"), EDIT_PASTE); + edit_menu->get_popup()->add_separator(); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/select_all"), EDIT_SELECT_ALL); edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/cut", TTR("Cut"), KEY_MASK_CMD | KEY_X), EDIT_CUT); - edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/copy", TTR("Copy"), KEY_MASK_CMD | KEY_C), EDIT_COPY); - edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/paste", TTR("Paste"), KEY_MASK_CMD | KEY_V), EDIT_PASTE); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_up"), EDIT_MOVE_LINE_UP); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_down"), EDIT_MOVE_LINE_DOWN); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_left"), EDIT_INDENT_LEFT); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_right"), EDIT_INDENT_RIGHT); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/delete_line"), EDIT_DELETE_LINE); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/clone_down"), EDIT_CLONE_DOWN); edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/select_all", TTR("Select All"), KEY_MASK_CMD | KEY_A), EDIT_SELECT_ALL); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/complete_symbol"), EDIT_COMPLETE); + edit_menu->get_popup()->connect("id_pressed", this, "_menu_option"); search_menu = memnew(MenuButton); - hbc->add_child(search_menu); - search_menu->set_position(Point2(38, -1)); + //search_menu->set_position(Point2(38, -1)); search_menu->set_text(TTR("Search")); - search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find", TTR("Find.."), KEY_MASK_CMD | KEY_F), SEARCH_FIND); - search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), KEY_F3), SEARCH_FIND_NEXT); - search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_previous", TTR("Find Previous"), KEY_MASK_SHIFT | KEY_F3), SEARCH_FIND_PREV); - search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/replace", TTR("Replace.."), KEY_MASK_CMD | KEY_R), SEARCH_REPLACE); + + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find"), SEARCH_FIND); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_next"), SEARCH_FIND_NEXT); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace"), SEARCH_REPLACE); search_menu->get_popup()->add_separator(); - //search_menu->get_popup()->add_item("Locate Symbol..",SEARCH_LOCATE_SYMBOL,KEY_MASK_CMD|KEY_K); - search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/goto_line", TTR("Goto Line.."), KEY_MASK_CMD | KEY_L), SEARCH_GOTO_LINE); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE); search_menu->get_popup()->connect("id_pressed", this, "_menu_option"); + add_child(main_container); + main_container->add_child(hbc); + hbc->add_child(search_menu); + hbc->add_child(edit_menu); + hbc->add_style_override("panel", p_node->get_gui_base()->get_stylebox("ScriptEditorPanel", "EditorStyles")); + main_container->add_child(shader_editor); + goto_line_dialog = memnew(GotoLineDialog); add_child(goto_line_dialog); - shader_editor = memnew(ShaderTextEditor); - add_child(shader_editor); - shader_editor->set_v_size_flags(SIZE_EXPAND_FILL); - - shader_editor->connect("script_changed", this, "apply_shaders"); - EditorSettings::get_singleton()->connect("settings_changed", this, "_editor_settings_changed"); - _editor_settings_changed(); } @@ -504,7 +805,7 @@ void ShaderEditorPlugin::apply_changes() { ShaderEditorPlugin::ShaderEditorPlugin(EditorNode *p_node) { editor = p_node; - shader_editor = memnew(ShaderEditor); + shader_editor = memnew(ShaderEditor(p_node)); shader_editor->set_custom_minimum_size(Size2(0, 300)); button = editor->add_bottom_panel_item(TTR("Shader"), shader_editor); diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h index ab18784d9f..b191f5700f 100644 --- a/editor/plugins/shader_editor_plugin.h +++ b/editor/plugins/shader_editor_plugin.h @@ -33,6 +33,7 @@ #include "editor/code_editor.h" #include "editor/editor_plugin.h" #include "scene/gui/menu_button.h" +#include "scene/gui/panel_container.h" #include "scene/gui/tab_container.h" #include "scene/gui/text_edit.h" #include "scene/main/timer.h" @@ -61,9 +62,9 @@ public: ShaderTextEditor(); }; -class ShaderEditor : public VBoxContainer { +class ShaderEditor : public PanelContainer { - GDCLASS(ShaderEditor, VBoxContainer); + GDCLASS(ShaderEditor, PanelContainer); enum { @@ -73,6 +74,14 @@ class ShaderEditor : public VBoxContainer { EDIT_COPY, EDIT_PASTE, EDIT_SELECT_ALL, + EDIT_MOVE_LINE_UP, + EDIT_MOVE_LINE_DOWN, + EDIT_INDENT_LEFT, + EDIT_INDENT_RIGHT, + EDIT_DELETE_LINE, + EDIT_CLONE_DOWN, + EDIT_TOGGLE_COMMENT, + EDIT_COMPLETE, SEARCH_FIND, SEARCH_FIND_NEXT, SEARCH_FIND_PREV, @@ -84,6 +93,7 @@ class ShaderEditor : public VBoxContainer { MenuButton *edit_menu; MenuButton *search_menu; MenuButton *settings_menu; + PopupMenu *context_menu; uint64_t idle; GotoLineDialog *goto_line_dialog; @@ -100,6 +110,8 @@ class ShaderEditor : public VBoxContainer { protected: void _notification(int p_what); static void _bind_methods(); + void _make_context_menu(bool p_selection); + void _text_edit_gui_input(const Ref<InputEvent> &ev); public: void apply_shaders(); @@ -110,7 +122,7 @@ public: virtual Size2 get_minimum_size() const { return Size2(0, 200); } void save_external_data(); - ShaderEditor(); + ShaderEditor(EditorNode *p_node); }; class ShaderEditorPlugin : public EditorPlugin { diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 75c6961521..b87084e392 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -1255,7 +1255,6 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { Vector3 motion_mask; Plane plane; - bool plane_mv; switch (_edit.plane) { case TRANSFORM_VIEW: @@ -1376,7 +1375,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { Vector3 motion_mask; Plane plane; - bool plane_mv; + bool plane_mv = false; switch (_edit.plane) { case TRANSFORM_VIEW: diff --git a/misc/travis/android-tools-linux.sh b/misc/travis/android-tools-linux.sh new file mode 100755 index 0000000000..04fb2eee21 --- /dev/null +++ b/misc/travis/android-tools-linux.sh @@ -0,0 +1,107 @@ +#!/bin/bash + +# SDK +# https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip +# SHA-256 444e22ce8ca0f67353bda4b85175ed3731cae3ffa695ca18119cbacef1c1bea0 +# latest version available here: https://developer.android.com/studio/index.html + +# NDK +# https://dl.google.com/android/repository/android-ndk-r15c-linux-x86_64.zip +# SHA-1 0bf02d4e8b85fd770fd7b9b2cdec57f9441f27a2 +# latest version available here: https://developer.android.com/ndk/downloads/index.html + +BASH_RC=~/.bashrc +GODOT_BUILD_TOOLS_PATH=./godot-dev/build-tools +mkdir -p $GODOT_BUILD_TOOLS_PATH +cd $GODOT_BUILD_TOOLS_PATH + +ANDROID_BASE_URL=http://dl.google.com/android/repository + +ANDROID_SDK_RELEASE=3859397 +ANDROID_SDK_DIR=android-sdk +ANDROID_SDK_FILENAME=sdk-tools-linux-$ANDROID_SDK_RELEASE.zip +ANDROID_SDK_URL=$ANDROID_BASE_URL/$ANDROID_SDK_FILENAME +ANDROID_SDK_PATH=$GODOT_BUILD_TOOLS_PATH/$ANDROID_SDK_DIR +ANDROID_SDK_SHA256=444e22ce8ca0f67353bda4b85175ed3731cae3ffa695ca18119cbacef1c1bea0 + +ANDROID_NDK_RELEASE=r15c +ANDROID_NDK_DIR=android-ndk +ANDROID_NDK_FILENAME=android-ndk-$ANDROID_NDK_RELEASE-linux-x86_64.zip +ANDROID_NDK_URL=$ANDROID_BASE_URL/$ANDROID_NDK_FILENAME +ANDROID_NDK_PATH=$GODOT_BUILD_TOOLS_PATH/$ANDROID_NDK_DIR +ANDROID_NDK_SHA1=0bf02d4e8b85fd770fd7b9b2cdec57f9441f27a2 + +echo +echo "Download and install Android development tools ..." +echo + +if [ ! -e $ANDROID_SDK_FILENAME ]; then + echo "Downloading: Android SDK ..." + curl -L -O $ANDROID_SDK_URL +else + echo $ANDROID_SDK_SHA1 $ANDROID_SDK_FILENAME > $ANDROID_SDK_FILENAME.sha1 + if [ $(shasum -a 256 < $ANDROID_SDK_FILENAME | awk '{print $1;}') != $ANDROID_SDK_SHA1 ]; then + echo "Downloading: Android SDK ..." + curl -L -O $ANDROID_SDK_URL + fi +fi + +if [ ! -d $ANDROID_SDK_DIR ]; then + echo "Extracting: Android SDK ..." + unzip -qq $ANDROID_SDK_FILENAME -d $ANDROID_SDK_DIR + echo +fi + +if [ ! -e $ANDROID_NDK_FILENAME ]; then + echo "Downloading: Android NDK ..." + curl -L -O $ANDROID_NDK_URL +else + echo $ANDROID_NDK_MD5 $ANDROID_NDK_FILENAME > $ANDROID_NDK_FILENAME.md5 + if [ $(shasum -a 1 < $ANDROID_NDK_FILENAME | awk '{print $1;}') != $ANDROID_NDK_SHA1 ]; then + echo "Downloading: Android NDK ..." + curl -L -O $ANDROID_NDK_URL + fi +fi + +if [ ! -d $ANDROID_NDK_DIR ]; then + echo "Extracting: Android NDK ..." + unzip -qq $ANDROID_NDK_FILENAME + mv android-ndk-$ANDROID_NDK_RELEASE $ANDROID_NDK_DIR + echo +fi + +echo "Installing: Android Tools ..." +#$ANDROID_SDK_DIR/tools/bin/sdkmanager --all +yes | $ANDROID_SDK_DIR/tools/bin/sdkmanager --licenses > /dev/null +$ANDROID_SDK_DIR/tools/bin/sdkmanager 'tools' > /dev/null +$ANDROID_SDK_DIR/tools/bin/sdkmanager 'platform-tools' > /dev/null +$ANDROID_SDK_DIR/tools/bin/sdkmanager 'build-tools;26.0.2' > /dev/null +echo + +EXPORT_VAL="export ANDROID_HOME=$ANDROID_SDK_PATH" +if ! grep -q "^$EXPORT_VAL" $BASH_RC; then + echo $EXPORT_VAL >> $BASH_RC +fi +#eval $EXPORT_VAL + +EXPORT_VAL="export ANDROID_NDK_ROOT=$ANDROID_NDK_PATH" +if ! grep -q "^$EXPORT_VAL" $BASH_RC; then + echo $EXPORT_VAL >> $BASH_RC +fi +#eval $EXPORT_VAL + +EXPORT_VAL="export PATH=$PATH:$ANDROID_SDK_PATH/tools" +if ! grep -q "^export PATH=.*$ANDROID_SDK_PATH/tools.*" $BASH_RC; then + echo $EXPORT_VAL >> $BASH_RC +fi +#eval $EXPORT_VAL + +EXPORT_VAL="export PATH=$PATH:$ANDROID_SDK_PATH/tools/bin" +if ! grep -q "^export PATH=.*$ANDROID_SDK_PATH/tools/bin.*" $BASH_RC; then + echo $EXPORT_VAL >> $BASH_RC +fi +#eval $EXPORT_VAL + +echo +echo "Done!" +echo diff --git a/misc/travis/android-tools-osx.sh b/misc/travis/android-tools-osx.sh new file mode 100755 index 0000000000..96125a3a3f --- /dev/null +++ b/misc/travis/android-tools-osx.sh @@ -0,0 +1,107 @@ +#!/bin/bash + +# SDK +# https://dl.google.com/android/repository/sdk-tools-darwin-3859397.zip +# SHA-256 4a81754a760fce88cba74d69c364b05b31c53d57b26f9f82355c61d5fe4b9df9 +# latest version available here: https://developer.android.com/studio/index.html + +# NDK +# https://dl.google.com/android/repository/android-ndk-r15c-darwin-x86_64.zip +# SHA-1 ea4b5d76475db84745aa8828000d009625fc1f98 +# latest version available here: https://developer.android.com/ndk/downloads/index.html + +BASH_RC=~/.bashrc +GODOT_BUILD_TOOLS_PATH=./godot-dev/build-tools +mkdir -p $GODOT_BUILD_TOOLS_PATH +cd $GODOT_BUILD_TOOLS_PATH + +ANDROID_BASE_URL=http://dl.google.com/android/repository + +ANDROID_SDK_RELEASE=3859397 +ANDROID_SDK_DIR=android-sdk +ANDROID_SDK_FILENAME=sdk-tools-darwin-$ANDROID_SDK_RELEASE.zip +ANDROID_SDK_URL=$ANDROID_BASE_URL/$ANDROID_SDK_FILENAME +ANDROID_SDK_PATH=$GODOT_BUILD_TOOLS_PATH/$ANDROID_SDK_DIR +ANDROID_SDK_SHA256=4a81754a760fce88cba74d69c364b05b31c53d57b26f9f82355c61d5fe4b9df9 + +ANDROID_NDK_RELEASE=r15c +ANDROID_NDK_DIR=android-ndk +ANDROID_NDK_FILENAME=android-ndk-$ANDROID_NDK_RELEASE-darwin-x86_64.zip +ANDROID_NDK_URL=$ANDROID_BASE_URL/$ANDROID_NDK_FILENAME +ANDROID_NDK_PATH=$GODOT_BUILD_TOOLS_PATH/$ANDROID_NDK_DIR +ANDROID_NDK_SHA1=ea4b5d76475db84745aa8828000d009625fc1f98 + +echo +echo "Download and install Android development tools ..." +echo + +if [ ! -e $ANDROID_SDK_FILENAME ]; then + echo "Downloading: Android SDK ..." + curl -L -O $ANDROID_SDK_URL +else + echo $ANDROID_SDK_SHA1 $ANDROID_SDK_FILENAME > $ANDROID_SDK_FILENAME.sha1 + if [ $(shasum -a 256 < $ANDROID_SDK_FILENAME | awk '{print $1;}') != $ANDROID_SDK_SHA1 ]; then + echo "Downloading: Android SDK ..." + curl -L -O $ANDROID_SDK_URL + fi +fi + +if [ ! -d $ANDROID_SDK_DIR ]; then + echo "Extracting: Android SDK ..." + mkdir -p $ANDROID_SDK_DIR && tar -xf $ANDROID_SDK_FILENAME -C $ANDROID_SDK_DIR + echo +fi + +if [ ! -e $ANDROID_NDK_FILENAME ]; then + echo "Downloading: Android NDK ..." + curl -L -O $ANDROID_NDK_URL +else + echo $ANDROID_NDK_MD5 $ANDROID_NDK_FILENAME > $ANDROID_NDK_FILENAME.md5 + if [ $(shasum -a 1 < $ANDROID_NDK_FILENAME | awk '{print $1;}') != $ANDROID_NDK_SHA1 ]; then + echo "Downloading: Android NDK ..." + curl -L -O $ANDROID_NDK_URL + fi +fi + +if [ ! -d $ANDROID_NDK_DIR ]; then + echo "Extracting: Android NDK ..." + tar -xf $ANDROID_NDK_FILENAME + mv android-ndk-$ANDROID_NDK_RELEASE $ANDROID_NDK_DIR + echo +fi + +echo "Installing: Android Tools ..." +#$ANDROID_SDK_DIR/tools/bin/sdkmanager --all +yes | $ANDROID_SDK_DIR/tools/bin/sdkmanager --licenses > /dev/null +$ANDROID_SDK_DIR/tools/bin/sdkmanager 'tools' > /dev/null +$ANDROID_SDK_DIR/tools/bin/sdkmanager 'platform-tools' > /dev/null +$ANDROID_SDK_DIR/tools/bin/sdkmanager 'build-tools;26.0.2' > /dev/null +echo + +EXPORT_VAL="export ANDROID_HOME=$ANDROID_SDK_PATH" +if ! grep -q "^$EXPORT_VAL" $BASH_RC; then + echo $EXPORT_VAL >> $BASH_RC +fi +#eval $EXPORT_VAL + +EXPORT_VAL="export ANDROID_NDK_ROOT=$ANDROID_NDK_PATH" +if ! grep -q "^$EXPORT_VAL" $BASH_RC; then + echo $EXPORT_VAL >> $BASH_RC +fi +#eval $EXPORT_VAL + +EXPORT_VAL="export PATH=$PATH:$ANDROID_SDK_PATH/tools" +if ! grep -q "^export PATH=.*$ANDROID_SDK_PATH/tools.*" $BASH_RC; then + echo $EXPORT_VAL >> $BASH_RC +fi +#eval $EXPORT_VAL + +EXPORT_VAL="export PATH=$PATH:$ANDROID_SDK_PATH/tools/bin" +if ! grep -q "^export PATH=.*$ANDROID_SDK_PATH/tools/bin.*" $BASH_RC; then + echo $EXPORT_VAL >> $BASH_RC +fi +#eval $EXPORT_VAL + +echo +echo "Done!" +echo diff --git a/misc/travis/ccache-osx.sh b/misc/travis/ccache-osx.sh new file mode 100755 index 0000000000..5ce7a80cbc --- /dev/null +++ b/misc/travis/ccache-osx.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +echo +echo "Download and install ccache ..." +echo + +echo "Downloading sources ..." +curl -L -O https://www.samba.org/ftp/ccache/ccache-3.3.4.tar.gz # latest version available here: https://ccache.samba.org/download.html + +echo "Extracting to build directory ..." +tar xzf ccache-3.3.4.tar.gz +cd ccache-3.3.4 + +echo "Compiling sources ..." +./configure --prefix=/usr/local --with-bundled-zlib > /dev/null +make + +echo "Installing ..." + +mkdir /usr/local/opt/ccache + +mkdir /usr/local/opt/ccache/bin +cp ccache /usr/local/opt/ccache/bin +ln -s /usr/local/opt/ccache/bin/ccache /usr/local/bin/ccache + +mkdir /usr/local/opt/ccache/libexec +links=( + clang + clang++ + cc + gcc gcc2 gcc3 gcc-3.3 gcc-4.0 gcc-4.2 gcc-4.3 gcc-4.4 gcc-4.5 gcc-4.6 gcc-4.7 gcc-4.8 gcc-4.9 gcc-5 gcc-6 gcc-7 + c++ c++3 c++-3.3 c++-4.0 c++-4.2 c++-4.3 c++-4.4 c++-4.5 c++-4.6 c++-4.7 c++-4.8 c++-4.9 c++-5 c++-6 c++-7 + g++ g++2 g++3 g++-3.3 g++-4.0 g++-4.2 g++-4.3 g++-4.4 g++-4.5 g++-4.6 g++-4.7 g++-4.8 g++-4.9 g++-5 g++-6 g++-7 +) +for link in "${links[@]}"; do + ln -s ../bin/ccache /usr/local/opt/ccache/libexec/$link +done +#/usr/local/bin/ccache -M 2G +cd $TRAVIS_BUILD_DIR + +echo +echo "Done!" +echo diff --git a/misc/travis/scons-local-osx.sh b/misc/travis/scons-local-osx.sh new file mode 100755 index 0000000000..d9d7d98b38 --- /dev/null +++ b/misc/travis/scons-local-osx.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +echo +echo "Download and install Scons local package ..." +echo + +echo "Downloading sources ..." +curl -L -O http://prdownloads.sourceforge.net/scons/scons-local-3.0.0.zip # latest version available here: http://scons.org/pages/download.html + +echo "Extracting to build directory ..." +unzip -qq -n scons-local-3.0.0.zip -d $TRAVIS_BUILD_DIR/scons-local + +echo "Installing symlinks ..." +ln -s $TRAVIS_BUILD_DIR/scons-local/scons.py /usr/local/bin/scons + +echo +echo "Done!" +echo diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub index 88588417d1..2755930a55 100644 --- a/modules/gdnative/SCsub +++ b/modules/gdnative/SCsub @@ -40,11 +40,13 @@ def _build_gdnative_api_struct_header(api): '\tunsigned int minor;', '} godot_gdnative_api_version;', '', - 'typedef struct godot_gdnative_api_struct {', + 'typedef struct godot_gdnative_api_struct godot_gdnative_api_struct;', + '', + 'struct godot_gdnative_api_struct {', '\tunsigned int type;', '\tgodot_gdnative_api_version version;', '\tconst godot_gdnative_api_struct *next;', - '} godot_gdnative_api_struct;', + '};', '', 'enum GDNATIVE_API_TYPES {', '\tGDNATIVE_' + api['core']['type'] + ',' diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp index 346b7d326a..655e785174 100644 --- a/modules/gdscript/gd_editor.cpp +++ b/modules/gdscript/gd_editor.cpp @@ -365,7 +365,7 @@ String GDScriptLanguage::make_function(const String &p_class, const String &p_na } s += " "; } - s += "):\n\tpass # replace with function body\n"; + s += "):\n" + _get_indentation() + "pass # replace with function body\n"; return s; } diff --git a/modules/ogg/config.py b/modules/ogg/config.py index ef5daca05c..fb920482f5 100644 --- a/modules/ogg/config.py +++ b/modules/ogg/config.py @@ -1,7 +1,6 @@ def can_build(platform): -# return True - return False + return True def configure(env): diff --git a/modules/opus/SCsub b/modules/opus/SCsub index fee06bd267..6f643ef08c 100644 --- a/modules/opus/SCsub +++ b/modules/opus/SCsub @@ -3,6 +3,9 @@ Import('env') Import('env_modules') + +stub = True + env_opus = env_modules.Clone() # Thirdparty source files @@ -212,5 +215,9 @@ if env['builtin_opus']: if env['builtin_libogg']: env_opus.Append(CPPPATH=["#thirdparty/libogg"]) -# Module files -env_opus.add_source_files(env.modules_sources, "*.cpp") +if not stub: + # Module files + env_opus.add_source_files(env.modules_sources, "*.cpp") +else: + # Module files + env_opus.add_source_files(env.modules_sources, "stub/register_types.cpp") diff --git a/modules/opus/config.py b/modules/opus/config.py index ef5daca05c..fb920482f5 100644 --- a/modules/opus/config.py +++ b/modules/opus/config.py @@ -1,7 +1,6 @@ def can_build(platform): -# return True - return False + return True def configure(env): diff --git a/modules/opus/stub/register_types.cpp b/modules/opus/stub/register_types.cpp new file mode 100644 index 0000000000..c5ae3e274e --- /dev/null +++ b/modules/opus/stub/register_types.cpp @@ -0,0 +1,36 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 "register_types.h" + +// Dummy module as libvorbis is needed by other modules (theora ...) + +void register_opus_types() {} + +void unregister_opus_types() {} diff --git a/modules/opus/stub/register_types.h b/modules/opus/stub/register_types.h new file mode 100644 index 0000000000..4517dc5df7 --- /dev/null +++ b/modules/opus/stub/register_types.h @@ -0,0 +1,31 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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. */ +/*************************************************************************/ +void register_opus_types(); +void unregister_opus_types(); diff --git a/modules/theora/config.py b/modules/theora/config.py index 8eefe81288..fb920482f5 100644 --- a/modules/theora/config.py +++ b/modules/theora/config.py @@ -1,7 +1,6 @@ def can_build(platform): -# return True - return False + return True def configure(env): diff --git a/modules/theora/register_types.cpp b/modules/theora/register_types.cpp index ae6961b3da..c51b87b8fc 100644 --- a/modules/theora/register_types.cpp +++ b/modules/theora/register_types.cpp @@ -28,19 +28,18 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "register_types.h" - +#include "resource_importer_theora.h" #include "video_stream_theora.h" -static ResourceFormatLoaderVideoStreamTheora *theora_stream_loader = NULL; - void register_theora_types() { - theora_stream_loader = memnew(ResourceFormatLoaderVideoStreamTheora); - ResourceLoader::add_resource_format_loader(theora_stream_loader); +#ifdef TOOLS_ENABLED + Ref<ResourceImporterTheora> theora_import; + theora_import.instance(); + ResourceFormatImporter::get_singleton()->add_importer(theora_import); +#endif ClassDB::register_class<VideoStreamTheora>(); } void unregister_theora_types() { - - memdelete(theora_stream_loader); } diff --git a/modules/theora/resource_importer_theora.cpp b/modules/theora/resource_importer_theora.cpp new file mode 100644 index 0000000000..c25c0e7427 --- /dev/null +++ b/modules/theora/resource_importer_theora.cpp @@ -0,0 +1,89 @@ +/*************************************************************************/ +/* resource_importer_theora.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 "resource_importer_theora.h" + +#include "io/resource_saver.h" +#include "os/file_access.h" +#include "scene/resources/texture.h" + +String ResourceImporterTheora::get_importer_name() const { + + return "Theora"; +} + +String ResourceImporterTheora::get_visible_name() const { + + return "Theora"; +} +void ResourceImporterTheora::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("ogv"); + p_extensions->push_back("ogm"); +} + +String ResourceImporterTheora::get_save_extension() const { + return "ogvstr"; +} + +String ResourceImporterTheora::get_resource_type() const { + + return "VideoStreamTheora"; +} + +bool ResourceImporterTheora::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { + + return true; +} + +int ResourceImporterTheora::get_preset_count() const { + return 0; +} +String ResourceImporterTheora::get_preset_name(int p_idx) const { + + return String(); +} + +void ResourceImporterTheora::get_import_options(List<ImportOption> *r_options, int p_preset) const { + + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true)); +} + +Error ResourceImporterTheora::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files) { + + VideoStreamTheora *stream = memnew(VideoStreamTheora); + stream->set_file(p_source_file); + + Ref<VideoStreamTheora> ogv_stream = Ref<VideoStreamTheora>(stream); + + return ResourceSaver::save(p_save_path + ".ogvstr", ogv_stream); +} + +ResourceImporterTheora::ResourceImporterTheora() { +} diff --git a/modules/theora/resource_importer_theora.h b/modules/theora/resource_importer_theora.h new file mode 100644 index 0000000000..8bf0ad38c4 --- /dev/null +++ b/modules/theora/resource_importer_theora.h @@ -0,0 +1,57 @@ +/*************************************************************************/ +/* resource_importer_theora.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 RESOURCEIMPORTEROGGTHEORA_H +#define RESOURCEIMPORTEROGGTHEORA_H + +#include "video_stream_theora.h" + +#include "core/io/resource_import.h" + +class ResourceImporterTheora : public ResourceImporter { + GDCLASS(ResourceImporterTheora, ResourceImporter) +public: + virtual String get_importer_name() const; + virtual String get_visible_name() const; + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual String get_save_extension() const; + virtual String get_resource_type() const; + + virtual int get_preset_count() const; + virtual String get_preset_name(int p_idx) const; + + virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const; + virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; + + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL); + + ResourceImporterTheora(); +}; + +#endif // RESOURCEIMPORTEROGGTHEORA_H diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index c75bec31df..bc8ca23d60 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -406,20 +406,19 @@ void VideoStreamPlaybackTheora::update(float p_delta) { ogg_packet op; bool no_theora = false; + bool buffer_full = false; - while (vorbis_p) { + while (vorbis_p && !audio_done && !buffer_full) { int ret; float **pcm; - bool buffer_full = false; - /* if there's pending, decoded audio, grab it */ ret = vorbis_synthesis_pcmout(&vd, &pcm); if (ret > 0) { const int AUXBUF_LEN = 4096; int to_read = ret; - int16_t aux_buffer[AUXBUF_LEN]; + float aux_buffer[AUXBUF_LEN]; while (to_read) { @@ -429,11 +428,7 @@ void VideoStreamPlaybackTheora::update(float p_delta) { for (int j = 0; j < m; j++) { for (int i = 0; i < vi.channels; i++) { - - int val = Math::fast_ftoi(pcm[i][j] * 32767.f); - if (val > 32767) val = 32767; - if (val < -32768) val = -32768; - aux_buffer[count++] = val; + aux_buffer[count++] = pcm[i][j]; } } @@ -602,10 +597,9 @@ bool VideoStreamPlaybackTheora::is_playing() const { void VideoStreamPlaybackTheora::set_paused(bool p_paused) { paused = p_paused; - //pau = !p_paused; }; -bool VideoStreamPlaybackTheora::is_paused(bool p_paused) const { +bool VideoStreamPlaybackTheora::is_paused() const { return paused; }; @@ -733,32 +727,10 @@ VideoStreamPlaybackTheora::~VideoStreamPlaybackTheora() { memdelete(file); }; -RES ResourceFormatLoaderVideoStreamTheora::load(const String &p_path, const String &p_original_path, Error *r_error) { - if (r_error) - *r_error = ERR_FILE_CANT_OPEN; - - VideoStreamTheora *stream = memnew(VideoStreamTheora); - stream->set_file(p_path); - - if (r_error) - *r_error = OK; - - return Ref<VideoStreamTheora>(stream); -} +void VideoStreamTheora::_bind_methods() { -void ResourceFormatLoaderVideoStreamTheora::get_recognized_extensions(List<String> *p_extensions) const { + ClassDB::bind_method(D_METHOD("set_file", "file"), &VideoStreamTheora::set_file); + ClassDB::bind_method(D_METHOD("get_file"), &VideoStreamTheora::get_file); - p_extensions->push_back("ogm"); - p_extensions->push_back("ogv"); -} -bool ResourceFormatLoaderVideoStreamTheora::handles_type(const String &p_type) const { - return (p_type == "VideoStream" || p_type == "VideoStreamTheora"); -} - -String ResourceFormatLoaderVideoStreamTheora::get_resource_type(const String &p_path) const { - - String exl = p_path.get_extension().to_lower(); - if (exl == "ogm" || exl == "ogv") - return "VideoStreamTheora"; - return ""; + ADD_PROPERTY(PropertyInfo(Variant::STRING, "file", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_file", "get_file"); } diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h index 484a1a7fb9..ec0e5aa34a 100644 --- a/modules/theora/video_stream_theora.h +++ b/modules/theora/video_stream_theora.h @@ -36,6 +36,7 @@ #include "os/thread.h" #include "ring_buffer.h" #include "scene/resources/video_stream.h" +#include "servers/audio_server.h" #include <theora/theoradec.h> #include <vorbis/codec.h> @@ -129,7 +130,7 @@ public: virtual bool is_playing() const; virtual void set_paused(bool p_paused); - virtual bool is_paused(bool p_paused) const; + virtual bool is_paused() const; virtual void set_loop(bool p_enable); virtual bool has_loop() const; @@ -161,10 +162,14 @@ public: class VideoStreamTheora : public VideoStream { GDCLASS(VideoStreamTheora, VideoStream); + RES_BASE_EXTENSION("ogvstr"); String file; int audio_track; +protected: + static void _bind_methods(); + public: Ref<VideoStreamPlayback> instance_playback() { Ref<VideoStreamPlaybackTheora> pb = memnew(VideoStreamPlaybackTheora); @@ -174,17 +179,10 @@ public: } void set_file(const String &p_file) { file = p_file; } + String get_file() { return file; } void set_audio_track(int p_track) { audio_track = p_track; } VideoStreamTheora() { audio_track = 0; } }; -class ResourceFormatLoaderVideoStreamTheora : public ResourceFormatLoader { -public: - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); - virtual void get_recognized_extensions(List<String> *p_extensions) const; - virtual bool handles_type(const String &p_type) const; - virtual String get_resource_type(const String &p_path) const; -}; - #endif diff --git a/modules/vorbis/SCsub b/modules/vorbis/SCsub index 9d2d0feb92..55a112585b 100644 --- a/modules/vorbis/SCsub +++ b/modules/vorbis/SCsub @@ -5,6 +5,8 @@ Import('env_modules') env_vorbis = env_modules.Clone() +stub = True + # Thirdparty source files if env['builtin_libvorbis']: thirdparty_dir = "#thirdparty/libvorbis/" @@ -45,5 +47,9 @@ if env['builtin_libvorbis']: if env['builtin_libogg']: env_vorbis.Append(CPPPATH=["#thirdparty/libogg"]) -# Godot source files -env_vorbis.add_source_files(env.modules_sources, "*.cpp") +if not stub: + # Module files + env_vorbis.add_source_files(env.modules_sources, "*.cpp") +else: + # Module files + env_vorbis.add_source_files(env.modules_sources, "stub/register_types.cpp") diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp index 6235799fc2..9fb6fa8197 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp @@ -106,8 +106,6 @@ int AudioStreamPlaybackOGGVorbis::mix(int16_t *p_buffer, int p_frames) { break; } -//printf("to mix %i - mix me %i bytes\n",to_mix,to_mix*stream_channels*sizeof(int16_t)); - #ifdef BIG_ENDIAN_ENABLED long ret = ov_read(&vf, (char *)p_buffer, todo * stream_channels * sizeof(int16_t), 1, 2, 1, ¤t_section); #else @@ -359,7 +357,7 @@ void AudioStreamPlaybackOGGVorbis::set_paused(bool p_paused) { paused = p_paused; } -bool AudioStreamPlaybackOGGVorbis::is_paused(bool p_paused) const { +bool AudioStreamPlaybackOGGVorbis::is_paused() const { return paused; } diff --git a/modules/vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h index 79eadec56e..5000d03fd4 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.h +++ b/modules/vorbis/audio_stream_ogg_vorbis.h @@ -85,7 +85,7 @@ public: virtual void set_loop_restart_time(float p_time) { loop_restart_time = p_time; } virtual void set_paused(bool p_paused); - virtual bool is_paused(bool p_paused) const; + virtual bool is_paused() const; virtual void set_loop(bool p_enable); virtual bool has_loop() const; diff --git a/modules/vorbis/config.py b/modules/vorbis/config.py index ef5daca05c..fb920482f5 100644 --- a/modules/vorbis/config.py +++ b/modules/vorbis/config.py @@ -1,7 +1,6 @@ def can_build(platform): -# return True - return False + return True def configure(env): diff --git a/modules/vorbis/stub/register_types.cpp b/modules/vorbis/stub/register_types.cpp new file mode 100644 index 0000000000..b93d890436 --- /dev/null +++ b/modules/vorbis/stub/register_types.cpp @@ -0,0 +1,36 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 "register_types.h" + +// Dummy module as libvorbis is needed by other modules (theora ...) + +void register_vorbis_types() {} + +void unregister_vorbis_types() {} diff --git a/modules/vorbis/stub/register_types.h b/modules/vorbis/stub/register_types.h new file mode 100644 index 0000000000..e7cde7a66c --- /dev/null +++ b/modules/vorbis/stub/register_types.h @@ -0,0 +1,31 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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. */ +/*************************************************************************/ +void register_vorbis_types(); +void unregister_vorbis_types(); diff --git a/modules/webm/config.py b/modules/webm/config.py index ef5daca05c..fb920482f5 100644 --- a/modules/webm/config.py +++ b/modules/webm/config.py @@ -1,7 +1,6 @@ def can_build(platform): -# return True - return False + return True def configure(env): diff --git a/modules/webm/libvpx/SCsub b/modules/webm/libvpx/SCsub index fd8d762a5e..73ba17d184 100644 --- a/modules/webm/libvpx/SCsub +++ b/modules/webm/libvpx/SCsub @@ -298,7 +298,7 @@ if webm_cpu_x86: if not yasm_found: webm_cpu_x86 = False - print "YASM is necessary for WebM SIMD optimizations." + print("YASM is necessary for WebM SIMD optimizations.") webm_simd_optimizations = False @@ -345,7 +345,7 @@ if webm_cpu_arm: webm_simd_optimizations = True if webm_simd_optimizations == False: - print "WebM SIMD optimizations are disabled. Check if your CPU architecture, CPU bits or platform are supported!" + print("WebM SIMD optimizations are disabled. Check if your CPU architecture, CPU bits or platform are supported!") env_libvpx.add_source_files(env.modules_sources, libvpx_sources) diff --git a/modules/webm/register_types.cpp b/modules/webm/register_types.cpp index 892d1b8420..669c9997f1 100644 --- a/modules/webm/register_types.cpp +++ b/modules/webm/register_types.cpp @@ -28,19 +28,18 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "register_types.h" - +#include "resource_importer_webm.h" #include "video_stream_webm.h" -static ResourceFormatLoaderVideoStreamWebm *webm_stream_loader = NULL; - void register_webm_types() { - webm_stream_loader = memnew(ResourceFormatLoaderVideoStreamWebm); - ResourceLoader::add_resource_format_loader(webm_stream_loader); +#ifdef TOOLS_ENABLED + Ref<ResourceImporterWebm> webm_import; + webm_import.instance(); + ResourceFormatImporter::get_singleton()->add_importer(webm_import); +#endif ClassDB::register_class<VideoStreamWebm>(); } void unregister_webm_types() { - - memdelete(webm_stream_loader); } diff --git a/modules/webm/resource_importer_webm.cpp b/modules/webm/resource_importer_webm.cpp new file mode 100644 index 0000000000..5db3d4df2e --- /dev/null +++ b/modules/webm/resource_importer_webm.cpp @@ -0,0 +1,95 @@ +/*************************************************************************/ +/* resource_importer_webm.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 "resource_importer_webm.h" + +#include "io/resource_saver.h" +#include "os/file_access.h" +#include "scene/resources/texture.h" +#include "video_stream_webm.h" + +String ResourceImporterWebm::get_importer_name() const { + + return "Webm"; +} + +String ResourceImporterWebm::get_visible_name() const { + + return "Webm"; +} +void ResourceImporterWebm::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("webm"); +} + +String ResourceImporterWebm::get_save_extension() const { + return "webmstr"; +} + +String ResourceImporterWebm::get_resource_type() const { + + return "VideoStreamWebm"; +} + +bool ResourceImporterWebm::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { + + return true; +} + +int ResourceImporterWebm::get_preset_count() const { + return 0; +} +String ResourceImporterWebm::get_preset_name(int p_idx) const { + + return String(); +} + +void ResourceImporterWebm::get_import_options(List<ImportOption> *r_options, int p_preset) const { + + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true)); +} + +Error ResourceImporterWebm::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files) { + + FileAccess *f = FileAccess::open(p_source_file, FileAccess::READ); + if (!f) { + ERR_FAIL_COND_V(!f, ERR_CANT_OPEN); + } + memdelete(f); + + VideoStreamWebm *stream = memnew(VideoStreamWebm); + stream->set_file(p_source_file); + + Ref<VideoStreamWebm> webm_stream = Ref<VideoStreamWebm>(stream); + + return ResourceSaver::save(p_save_path + ".webmstr", webm_stream); +} + +ResourceImporterWebm::ResourceImporterWebm() { +} diff --git a/modules/webm/resource_importer_webm.h b/modules/webm/resource_importer_webm.h new file mode 100644 index 0000000000..4cedd1598d --- /dev/null +++ b/modules/webm/resource_importer_webm.h @@ -0,0 +1,55 @@ +/*************************************************************************/ +/* resource_importer_webm.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 RESOURCEIMPORTERWEBM_H +#define RESOURCEIMPORTERWEBM_H + +#include "io/resource_import.h" + +class ResourceImporterWebm : public ResourceImporter { + GDCLASS(ResourceImporterWebm, ResourceImporter) +public: + virtual String get_importer_name() const; + virtual String get_visible_name() const; + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual String get_save_extension() const; + virtual String get_resource_type() const; + + virtual int get_preset_count() const; + virtual String get_preset_name(int p_idx) const; + + virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const; + virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; + + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL); + + ResourceImporterWebm(); +}; + +#endif // RESOURCEIMPORTERWEBM_H diff --git a/modules/webm/video_stream_webm.cpp b/modules/webm/video_stream_webm.cpp index 2ec6b27471..0fc9df5b58 100644 --- a/modules/webm/video_stream_webm.cpp +++ b/modules/webm/video_stream_webm.cpp @@ -35,10 +35,13 @@ #include "mkvparser/mkvparser.h" #include "os/file_access.h" +#include "os/os.h" #include "project_settings.h" #include "thirdparty/misc/yuv2rgb.h" +#include "servers/audio_server.h" + #include <string.h> class MkvReader : public mkvparser::IMkvReader { @@ -47,6 +50,8 @@ public: MkvReader(const String &p_file) { file = FileAccess::open(p_file, FileAccess::READ); + + ERR_EXPLAIN("Failed loading resource: '" + p_file + "';"); ERR_FAIL_COND(!file); } ~MkvReader() { @@ -113,14 +118,14 @@ bool VideoStreamPlaybackWebm::open_file(const String &p_file) { webm = memnew(WebMDemuxer(new MkvReader(file_name), 0, audio_track)); if (webm->isOpen()) { - video = memnew(VPXDecoder(*webm, 8)); //TODO: Detect CPU threads + video = memnew(VPXDecoder(*webm, OS::get_singleton()->get_processor_count())); if (video->isOpen()) { audio = memnew(OpusVorbisDecoder(*webm)); if (audio->isOpen()) { audio_frame = memnew(WebMFrame); - pcm = (int16_t *)memalloc(sizeof(int16_t) * audio->getBufferSamples() * webm->getChannels()); + pcm = (float *)memalloc(sizeof(float) * audio->getBufferSamples() * webm->getChannels()); } else { memdelete(audio); @@ -183,7 +188,7 @@ void VideoStreamPlaybackWebm::set_paused(bool p_paused) { paused = p_paused; } -bool VideoStreamPlaybackWebm::is_paused(bool p_paused) const { +bool VideoStreamPlaybackWebm::is_paused() const { return paused; } @@ -222,11 +227,18 @@ Ref<Texture> VideoStreamPlaybackWebm::get_texture() { return texture; } + void VideoStreamPlaybackWebm::update(float p_delta) { if ((!playing || paused) || !video) return; + time += p_delta; + + if (time < video_pos) { + return; + } + bool audio_buffer_full = false; if (samples_offset > -1) { @@ -245,13 +257,15 @@ void VideoStreamPlaybackWebm::update(float p_delta) { } const bool hasAudio = (audio && mix_callback); - while ((hasAudio && (!audio_buffer_full || !has_enough_video_frames())) || (!hasAudio && video_frames_pos == 0)) { + while ((hasAudio && !audio_buffer_full && !has_enough_video_frames()) || + (!hasAudio && video_frames_pos == 0)) { - if (hasAudio && !audio_buffer_full && audio_frame->isValid() && audio->getPCMS16(*audio_frame, pcm, num_decoded_samples) && num_decoded_samples > 0) { + if (hasAudio && !audio_buffer_full && audio_frame->isValid() && + audio->getPCMF(*audio_frame, pcm, num_decoded_samples) && num_decoded_samples > 0) { const int mixed = mix_callback(mix_udata, pcm, num_decoded_samples); - if (mixed != num_decoded_samples) { + if (mixed != num_decoded_samples) { samples_offset = mixed; audio_buffer_full = true; } @@ -273,72 +287,61 @@ void VideoStreamPlaybackWebm::update(float p_delta) { ++video_frames_pos; }; - const double video_delay = video->getFramesDelay() * video_frame_delay; - - bool want_this_frame = false; - while (video_frames_pos > 0 && !want_this_frame) { + bool video_frame_done = false; + while (video_frames_pos > 0 && !video_frame_done) { WebMFrame *video_frame = video_frames[0]; - if (video_frame->time <= time + video_delay) { - if (video->decode(*video_frame)) { + // It seems VPXDecoder::decode has to be executed even though we might skip this frame + if (video->decode(*video_frame)) { - VPXDecoder::IMAGE_ERROR err; - VPXDecoder::Image image; + VPXDecoder::IMAGE_ERROR err; + VPXDecoder::Image image; - while ((err = video->getImage(image)) != VPXDecoder::NO_FRAME) { + if (should_process(*video_frame)) { - want_this_frame = (time - video_frame->time <= video_frame_delay); + if ((err = video->getImage(image)) != VPXDecoder::NO_FRAME) { - if (want_this_frame) { + if (err == VPXDecoder::NO_ERROR && image.w == webm->getWidth() && image.h == webm->getHeight()) { - if (err == VPXDecoder::NO_ERROR && image.w == webm->getWidth() && image.h == webm->getHeight()) { + PoolVector<uint8_t>::Write w = frame_data.write(); + bool converted = false; - PoolVector<uint8_t>::Write w = frame_data.write(); - bool converted = false; + if (image.chromaShiftW == 1 && image.chromaShiftH == 1) { - if (image.chromaShiftW == 1 && image.chromaShiftH == 1) { + yuv420_2_rgb8888(w.ptr(), image.planes[0], image.planes[2], image.planes[1], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2, 0); + // libyuv::I420ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); + converted = true; + } else if (image.chromaShiftW == 1 && image.chromaShiftH == 0) { - yuv420_2_rgb8888(w.ptr(), image.planes[0], image.planes[2], image.planes[1], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2, 0); - // libyuv::I420ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); - converted = true; - } else if (image.chromaShiftW == 1 && image.chromaShiftH == 0) { + yuv422_2_rgb8888(w.ptr(), image.planes[0], image.planes[2], image.planes[1], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2, 0); + // libyuv::I422ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); + converted = true; + } else if (image.chromaShiftW == 0 && image.chromaShiftH == 0) { - yuv422_2_rgb8888(w.ptr(), image.planes[0], image.planes[2], image.planes[1], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2, 0); - // libyuv::I422ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); - converted = true; - } else if (image.chromaShiftW == 0 && image.chromaShiftH == 0) { + yuv444_2_rgb8888(w.ptr(), image.planes[0], image.planes[2], image.planes[1], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2, 0); + // libyuv::I444ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); + converted = true; + } else if (image.chromaShiftW == 2 && image.chromaShiftH == 0) { - yuv444_2_rgb8888(w.ptr(), image.planes[0], image.planes[2], image.planes[1], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2, 0); - // libyuv::I444ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); - converted = true; - } else if (image.chromaShiftW == 2 && image.chromaShiftH == 0) { - - // libyuv::I411ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); - // converted = true; - } - - if (converted) - texture->set_data(Image(image.w, image.h, 0, Image::FORMAT_RGBA8, frame_data)); //Zero copy send to visual server + // libyuv::I411ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h); + // converted = true; } - break; + if (converted) { + Ref<Image> img = memnew(Image(image.w, image.h, 0, Image::FORMAT_RGBA8, frame_data)); + texture->set_data(img); //Zero copy send to visual server + video_frame_done = true; + } } } } - - video_frame_delay = video_frame->time - video_pos; - video_pos = video_frame->time; - - memmove(video_frames, video_frames + 1, (--video_frames_pos) * sizeof(void *)); - video_frames[video_frames_pos] = video_frame; - } else { - - break; } - } - time += p_delta; + video_pos = video_frame->time; + memmove(video_frames, video_frames + 1, (--video_frames_pos) * sizeof(void *)); + video_frames[video_frames_pos] = video_frame; + } if (video_frames_pos == 0 && webm->isEOS()) stop(); @@ -372,6 +375,11 @@ inline bool VideoStreamPlaybackWebm::has_enough_video_frames() const { return false; } +bool VideoStreamPlaybackWebm::should_process(WebMFrame &video_frame) { + const double audio_delay = AudioServer::get_singleton()->get_output_delay(); + return video_frame.time >= time + audio_delay + delay_compensation; +} + void VideoStreamPlaybackWebm::delete_pointers() { if (pcm) @@ -395,34 +403,6 @@ void VideoStreamPlaybackWebm::delete_pointers() { /**/ -RES ResourceFormatLoaderVideoStreamWebm::load(const String &p_path, const String &p_original_path, Error *r_error) { - - Ref<VideoStreamWebm> stream = memnew(VideoStreamWebm); - stream->set_file(p_path); - if (r_error) - *r_error = OK; - return stream; -} - -void ResourceFormatLoaderVideoStreamWebm::get_recognized_extensions(List<String> *p_extensions) const { - - p_extensions->push_back("webm"); -} -bool ResourceFormatLoaderVideoStreamWebm::handles_type(const String &p_type) const { - - return (p_type == "VideoStream" || p_type == "VideoStreamWebm"); -} - -String ResourceFormatLoaderVideoStreamWebm::get_resource_type(const String &p_path) const { - - const String exl = p_path.get_extension().to_lower(); - if (exl == "webm") - return "VideoStreamWebm"; - return ""; -} - -/**/ - VideoStreamWebm::VideoStreamWebm() : audio_track(0) {} @@ -439,6 +419,19 @@ void VideoStreamWebm::set_file(const String &p_file) { file = p_file; } +String VideoStreamWebm::get_file() { + + return file; +} + +void VideoStreamWebm::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_file", "file"), &VideoStreamWebm::set_file); + ClassDB::bind_method(D_METHOD("get_file"), &VideoStreamWebm::get_file); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "file", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_file", "get_file"); +} + void VideoStreamWebm::set_audio_track(int p_track) { audio_track = p_track; diff --git a/modules/webm/video_stream_webm.h b/modules/webm/video_stream_webm.h index fc0720967a..f7dd16a38f 100644 --- a/modules/webm/video_stream_webm.h +++ b/modules/webm/video_stream_webm.h @@ -60,7 +60,7 @@ class VideoStreamPlaybackWebm : public VideoStreamPlayback { PoolVector<uint8_t> frame_data; Ref<ImageTexture> texture; - int16_t *pcm; + float *pcm; public: VideoStreamPlaybackWebm(); @@ -74,7 +74,7 @@ public: virtual bool is_playing() const; virtual void set_paused(bool p_paused); - virtual bool is_paused(bool p_paused) const; + virtual bool is_paused() const; virtual void set_loop(bool p_enable); virtual bool has_loop() const; @@ -95,6 +95,7 @@ public: private: inline bool has_enough_video_frames() const; + bool should_process(WebMFrame &video_frame); void delete_pointers(); }; @@ -103,27 +104,21 @@ private: class VideoStreamWebm : public VideoStream { - GDCLASS(VideoStreamWebm, VideoStream) + GDCLASS(VideoStreamWebm, VideoStream); + RES_BASE_EXTENSION("webmstr"); String file; int audio_track; +protected: + static void _bind_methods(); + public: VideoStreamWebm(); virtual Ref<VideoStreamPlayback> instance_playback(); virtual void set_file(const String &p_file); + String get_file(); virtual void set_audio_track(int p_track); }; - -/**/ - -class ResourceFormatLoaderVideoStreamWebm : public ResourceFormatLoader { - -public: - virtual RES load(const String &p_path, const String &p_original_path, Error *r_error); - virtual void get_recognized_extensions(List<String> *p_extensions) const; - virtual bool handles_type(const String &p_type) const; - virtual String get_resource_type(const String &p_path) const; -}; diff --git a/platform/android/detect.py b/platform/android/detect.py index 966de832e8..a3ada5cf51 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -173,8 +173,15 @@ def configure(env): # For Clang to find NDK tools in preference of those system-wide env.PrependENVPath('PATH', tools_path) - env['CC'] = compiler_path + '/clang' - env['CXX'] = compiler_path + '/clang++' + ccache_path = os.environ.get("CCACHE") + if ccache_path == None: + env['CC'] = compiler_path + '/clang' + env['CXX'] = compiler_path + '/clang++' + else: + # there aren't any ccache wrappers available for Android, + # to enable caching we need to prepend the path to the ccache binary + env['CC'] = ccache_path + ' ' + compiler_path + '/clang' + env['CXX'] = ccache_path + ' ' + compiler_path + '/clang++' env['AR'] = tools_path + "/ar" env['RANLIB'] = tools_path + "/ranlib" env['AS'] = tools_path + "/as" diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py index 993a93ff89..d426b478bf 100644 --- a/platform/iphone/detect.py +++ b/platform/iphone/detect.py @@ -76,11 +76,22 @@ def configure(env): env['ENV']['PATH'] = env['IPHONEPATH'] + "/Developer/usr/bin/:" + env['ENV']['PATH'] - env['CC'] = '$IPHONEPATH/usr/bin/${ios_triple}clang' - env['CXX'] = '$IPHONEPATH/usr/bin/${ios_triple}clang++' - env['AR'] = '$IPHONEPATH/usr/bin/${ios_triple}ar' - env['RANLIB'] = '$IPHONEPATH/usr/bin/${ios_triple}ranlib' - env['S_compiler'] = '$IPHONEPATH/Developer/usr/bin/gcc' + compiler_path = '$IPHONEPATH/usr/bin/${ios_triple}' + s_compiler_path = '$IPHONEPATH/Developer/usr/bin/' + + ccache_path = os.environ.get("CCACHE") + if ccache_path == None: + env['CC'] = compiler_path + 'clang' + env['CXX'] = compiler_path + 'clang++' + env['S_compiler'] = s_compiler_path + 'gcc' + else: + # there aren't any ccache wrappers available for iOS, + # to enable caching we need to prepend the path to the ccache binary + env['CC'] = ccache_path + ' ' + compiler_path + 'clang' + env['CXX'] = ccache_path + ' ' + compiler_path + 'clang++' + env['S_compiler'] = ccache_path + ' ' + s_compiler_path + 'gcc' + env['AR'] = compiler_path + 'ar' + env['RANLIB'] = compiler_path + 'ranlib' ## Compile flags diff --git a/platform/osx/detect.py b/platform/osx/detect.py index 31032659b6..c24bd98bf6 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -84,8 +84,15 @@ def configure(env): else: # 64-bit, default basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-" - env['CC'] = basecmd + "cc" - env['CXX'] = basecmd + "c++" + ccache_path = os.environ.get("CCACHE") + if ccache_path == None: + env['CC'] = basecmd + "cc" + env['CXX'] = basecmd + "c++" + else: + # there aren't any ccache wrappers available for OS X cross-compile, + # to enable caching we need to prepend the path to the ccache binary + env['CC'] = ccache_path + ' ' + basecmd + "cc" + env['CXX'] = ccache_path + ' ' + basecmd + "c++" env['AR'] = basecmd + "ar" env['RANLIB'] = basecmd + "ranlib" env['AS'] = basecmd + "as" diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 80b7748078..6be3ff88d9 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -545,7 +545,14 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, float p_delta, f } else { - next_pos = Math::fposmod(next_pos, len); + float looped_next_pos = Math::fposmod(next_pos, len); + if (looped_next_pos == 0 && next_pos != 0) { + // Loop multiples of the length to it, rather than 0 + // so state at time=length is previewable in the editor + next_pos = len; + } else { + next_pos = looped_next_pos; + } } cd.pos = next_pos; diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 1ad1e3f638..9022d67a4a 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -347,12 +347,11 @@ void ScrollContainer::update_scrollbars() { } else { v_scroll->show(); + v_scroll->set_max(min.height); + v_scroll->set_page(size.height - hmin.height); scroll.y = v_scroll->get_value(); } - v_scroll->set_max(min.height); - v_scroll->set_page(size.height - hmin.height); - if (!scroll_h || min.width <= size.width - vmin.width) { h_scroll->hide(); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 02b203b5e3..5d429f9f91 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -3905,7 +3905,12 @@ void TextEdit::select(int p_from_line, int p_from_column, int p_to_line, int p_t update(); } - +void TextEdit::swap_lines(int line1, int line2) { + String tmp = get_line(line1); + String tmp2 = get_line(line2); + set_line(line2, tmp); + set_line(line1, tmp2); +} bool TextEdit::is_selection_active() const { return selection.active; diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index b90571e0ab..50f005ed6a 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -464,6 +464,7 @@ public: void select_all(); void select(int p_from_line, int p_from_column, int p_to_line, int p_to_column); void deselect(); + void swap_lines(int line1, int line2); void set_search_text(const String &p_search_text); void set_search_flags(uint32_t p_flags); diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp index 190ccd50d5..1b6bd30b58 100644 --- a/scene/gui/video_player.cpp +++ b/scene/gui/video_player.cpp @@ -42,44 +42,127 @@ void VideoPlayer::sp_set_mix_rate(int p_rate) { server_mix_rate = p_rate; } -bool VideoPlayer::sp_mix(int32_t *p_buffer, int p_frames) { - - if (resampler.is_ready()) { +bool VideoPlayer::mix(AudioFrame *p_buffer, int p_frames) { + + // Check the amount resampler can really handle. + // If it cannot, wait "wait_resampler_phase_limit" times. + // This mechanism contributes to smoother pause/unpause operation. + if (p_frames <= resampler.get_num_of_ready_frames() || + wait_resampler_limit <= wait_resampler) { + wait_resampler = 0; return resampler.mix(p_buffer, p_frames); } - + wait_resampler++; return false; } -int VideoPlayer::_audio_mix_callback(void *p_udata, const int16_t *p_data, int p_frames) { +// Called from main thread (eg VideoStreamPlaybackWebm::update) +int VideoPlayer::_audio_mix_callback(void *p_udata, const float *p_data, int p_frames) { VideoPlayer *vp = (VideoPlayer *)p_udata; - int todo = MIN(vp->resampler.get_todo(), p_frames); + int todo = MIN(vp->resampler.get_writer_space(), p_frames); - int16_t *wb = vp->resampler.get_write_buffer(); + float *wb = vp->resampler.get_write_buffer(); int c = vp->resampler.get_channel_count(); for (int i = 0; i < todo * c; i++) { wb[i] = p_data[i]; } vp->resampler.write(todo); + return todo; } +// Called from audio thread +void VideoPlayer::_mix_audio() { + + if (!stream.is_valid()) { + return; + } + if (!playback.is_valid() || !playback->is_playing() || playback->is_paused()) { + return; + } + + AudioFrame *buffer = mix_buffer.ptr(); + int buffer_size = mix_buffer.size(); + + // Resample + if (!mix(buffer, buffer_size)) + return; + + AudioFrame vol = AudioFrame(volume, volume); + + // Copy to server's audio buffer + switch (AudioServer::get_singleton()->get_speaker_mode()) { + + case AudioServer::SPEAKER_MODE_STEREO: { + AudioFrame *target = AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 0); + + for (int j = 0; j < buffer_size; j++) { + + target[j] += buffer[j] * vol; + } + + } break; + case AudioServer::SPEAKER_SURROUND_51: { + + AudioFrame *targets[2] = { + AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 1), + AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 2), + }; + + for (int j = 0; j < buffer_size; j++) { + + AudioFrame frame = buffer[j] * vol; + targets[0][j] = frame; + targets[1][j] = frame; + } + } break; + case AudioServer::SPEAKER_SURROUND_71: { + + AudioFrame *targets[3] = { + AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 1), + AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 2), + AudioServer::get_singleton()->thread_get_channel_mix_buffer(bus_index, 3) + }; + + for (int j = 0; j < buffer_size; j++) { + + AudioFrame frame = buffer[j] * vol; + targets[0][j] += frame; + targets[1][j] += frame; + targets[2][j] += frame; + } + + } break; + } +} + void VideoPlayer::_notification(int p_notification) { switch (p_notification) { case NOTIFICATION_ENTER_TREE: { + AudioServer::get_singleton()->add_callback(_mix_audios, this); + if (stream.is_valid() && autoplay && !Engine::get_singleton()->is_editor_hint()) { play(); } + + } break; + + case NOTIFICATION_EXIT_TREE: { + + AudioServer::get_singleton()->remove_callback(_mix_audios, this); + } break; case NOTIFICATION_INTERNAL_PROCESS: { + bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus); + if (stream.is_null()) return; if (paused) @@ -87,10 +170,11 @@ void VideoPlayer::_notification(int p_notification) { if (!playback->is_playing()) return; - double audio_time = USEC_TO_SEC(OS::get_singleton()->get_ticks_usec()); //AudioServer::get_singleton()->get_mix_time(); + double audio_time = USEC_TO_SEC(OS::get_singleton()->get_ticks_usec()); double delta = last_audio_time == 0 ? 0 : audio_time - last_audio_time; last_audio_time = audio_time; + if (delta == 0) return; @@ -135,6 +219,9 @@ bool VideoPlayer::has_expand() const { void VideoPlayer::set_stream(const Ref<VideoStream> &p_stream) { stop(); + AudioServer::get_singleton()->lock(); + mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size()); + AudioServer::get_singleton()->unlock(); stream = p_stream; if (stream.is_valid()) { @@ -309,6 +396,40 @@ bool VideoPlayer::has_autoplay() const { return autoplay; }; +void VideoPlayer::set_bus(const StringName &p_bus) { + + //if audio is active, must lock this + AudioServer::get_singleton()->lock(); + bus = p_bus; + AudioServer::get_singleton()->unlock(); +} + +StringName VideoPlayer::get_bus() const { + + for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { + if (AudioServer::get_singleton()->get_bus_name(i) == bus) { + return bus; + } + } + return "Master"; +} + +void VideoPlayer::_validate_property(PropertyInfo &property) const { + + if (property.name == "bus") { + + String options; + for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { + if (i > 0) + options += ","; + String name = AudioServer::get_singleton()->get_bus_name(i); + options += name; + } + + property.hint_string = options; + } +} + void VideoPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stream", "stream"), &VideoPlayer::set_stream); @@ -345,6 +466,9 @@ void VideoPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_buffering_msec", "msec"), &VideoPlayer::set_buffering_msec); ClassDB::bind_method(D_METHOD("get_buffering_msec"), &VideoPlayer::get_buffering_msec); + ClassDB::bind_method(D_METHOD("set_bus", "bus"), &VideoPlayer::set_bus); + ClassDB::bind_method(D_METHOD("get_bus"), &VideoPlayer::get_bus); + ClassDB::bind_method(D_METHOD("get_video_texture"), &VideoPlayer::get_video_texture); ADD_PROPERTY(PropertyInfo(Variant::INT, "audio_track", PROPERTY_HINT_RANGE, "0,128,1"), "set_audio_track", "get_audio_track"); @@ -354,6 +478,7 @@ void VideoPlayer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "has_autoplay"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "paused"), "set_paused", "is_paused"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand"), "set_expand", "has_expand"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus"); } VideoPlayer::VideoPlayer() { @@ -372,6 +497,9 @@ VideoPlayer::VideoPlayer() { // internal_stream.player=this; // stream_rid=AudioServer::get_singleton()->audio_stream_create(&internal_stream); last_audio_time = 0; + + wait_resampler = 0; + wait_resampler_limit = 2; }; VideoPlayer::~VideoPlayer() { diff --git a/scene/gui/video_player.h b/scene/gui/video_player.h index f04e90365f..74e2f14e58 100644 --- a/scene/gui/video_player.h +++ b/scene/gui/video_player.h @@ -33,17 +33,24 @@ #include "scene/gui/control.h" #include "scene/resources/video_stream.h" #include "servers/audio/audio_rb_resampler.h" +#include "servers/audio_server.h" class VideoPlayer : public Control { GDCLASS(VideoPlayer, Control); + struct Output { + + AudioFrame vol; + int bus_index; + Viewport *viewport; //pointer only used for reference to previous mix + }; Ref<VideoStreamPlayback> playback; Ref<VideoStream> stream; int sp_get_channel_count() const; void sp_set_mix_rate(int p_rate); //notify the stream of the mix rate - bool sp_mix(int32_t *p_buffer, int p_frames); + bool mix(AudioFrame *p_buffer, int p_frames); RID stream_rid; @@ -51,6 +58,8 @@ class VideoPlayer : public Control { Ref<Image> last_frame; AudioRBResampler resampler; + Vector<AudioFrame> mix_buffer; + int wait_resampler, wait_resampler_limit; bool paused; bool autoplay; @@ -61,12 +70,18 @@ class VideoPlayer : public Control { int buffering_ms; int server_mix_rate; int audio_track; + int bus_index; + + StringName bus; - static int _audio_mix_callback(void *p_udata, const int16_t *p_data, int p_frames); + void _mix_audio(); + static int _audio_mix_callback(void *p_udata, const float *p_data, int p_frames); + static void _mix_audios(void *self) { reinterpret_cast<VideoPlayer *>(self)->_mix_audio(); } protected: static void _bind_methods(); void _notification(int p_notification); + void _validate_property(PropertyInfo &property) const; public: Size2 get_minimum_size() const; @@ -104,6 +119,9 @@ public: void set_buffering_msec(int p_msec); int get_buffering_msec() const; + void set_bus(const StringName &p_bus); + StringName get_bus() const; + VideoPlayer(); ~VideoPlayer(); }; diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 1889c09d9e..d38c688241 100755 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -2411,7 +2411,9 @@ void Node::_replace_connections_target(Node *p_new_target) { if (c.flags & CONNECT_PERSIST) { c.source->disconnect(c.signal, this, c.method); - ERR_CONTINUE(!p_new_target->has_method(c.method)); + bool valid = p_new_target->has_method(c.method) || p_new_target->get_script().is_null() || Ref<Script>(p_new_target->get_script())->has_method(c.method); + ERR_EXPLAIN("Attempt to connect signal \'" + c.source->get_class() + "." + c.signal + "\' to nonexistent method \'" + c.target->get_class() + "." + c.method + "\'"); + ERR_CONTINUE(!valid); c.source->connect(c.signal, p_new_target, c.method, c.binds, c.flags); } } diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index ba356d89b1..8e3899315c 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -1299,14 +1299,16 @@ void QuadMesh::_create_mesh_array(Array &p_arr) const { tangents.resize(4 * 4); uvs.resize(4); - for (int i = 0; i < 4; i++) { + Vector2 _size = Vector2(size.x / 2.0f, size.y / 2.0f); - static const Vector3 quad_faces[4] = { - Vector3(-1, -1, 0), - Vector3(-1, 1, 0), - Vector3(1, 1, 0), - Vector3(1, -1, 0), - }; + Vector3 quad_faces[4] = { + Vector3(-_size.x, -_size.y, 0), + Vector3(-_size.x, _size.y, 0), + Vector3(_size.x, _size.y, 0), + Vector3(_size.x, -_size.y, 0), + }; + + for (int i = 0; i < 4; i++) { faces.set(i, quad_faces[i]); normals.set(i, Vector3(0, 0, 1)); @@ -1325,18 +1327,30 @@ void QuadMesh::_create_mesh_array(Array &p_arr) const { uvs.set(i, quad_uv[i]); } - p_arr[ARRAY_VERTEX] = faces; - p_arr[ARRAY_NORMAL] = normals; - p_arr[ARRAY_TANGENT] = tangents; - p_arr[ARRAY_TEX_UV] = uvs; + p_arr[VS::ARRAY_VERTEX] = faces; + p_arr[VS::ARRAY_NORMAL] = normals; + p_arr[VS::ARRAY_TANGENT] = tangents; + p_arr[VS::ARRAY_TEX_UV] = uvs; }; void QuadMesh::_bind_methods() { - // nothing here yet... + ClassDB::bind_method(D_METHOD("set_size", "size"), &QuadMesh::set_size); + ClassDB::bind_method(D_METHOD("get_size"), &QuadMesh::get_size); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size"); } QuadMesh::QuadMesh() { primitive_type = PRIMITIVE_TRIANGLE_FAN; + size = Size2(1.0, 1.0); +} + +void QuadMesh::set_size(const Size2 &p_size) { + size = p_size; + _request_update(); +} + +Size2 QuadMesh::get_size() const { + return size; } /** diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index 38a5695883..f0c8935261 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -263,7 +263,7 @@ class QuadMesh : public PrimitiveMesh { GDCLASS(QuadMesh, PrimitiveMesh) private: - // nothing? really? Maybe add size some day atleast... :) + Size2 size; protected: static void _bind_methods(); @@ -271,6 +271,9 @@ protected: public: QuadMesh(); + + void set_size(const Size2 &p_size); + Size2 get_size() const; }; /** diff --git a/scene/resources/video_stream.h b/scene/resources/video_stream.h index 3f79858056..fbe52909e7 100644 --- a/scene/resources/video_stream.h +++ b/scene/resources/video_stream.h @@ -40,7 +40,7 @@ protected: static void _bind_methods(); public: - typedef int (*AudioMixCallback)(void *p_udata, const int16_t *p_data, int p_frames); + typedef int (*AudioMixCallback)(void *p_udata, const float *p_data, int p_frames); virtual void stop() = 0; virtual void play() = 0; @@ -48,7 +48,7 @@ public: virtual bool is_playing() const = 0; virtual void set_paused(bool p_paused) = 0; - virtual bool is_paused(bool p_paused) const = 0; + virtual bool is_paused() const = 0; virtual void set_loop(bool p_enable) = 0; virtual bool has_loop() const = 0; diff --git a/servers/audio/audio_rb_resampler.cpp b/servers/audio/audio_rb_resampler.cpp index 113e356612..b0b94a1f49 100644 --- a/servers/audio/audio_rb_resampler.cpp +++ b/servers/audio/audio_rb_resampler.cpp @@ -28,6 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "audio_rb_resampler.h" +#include "core/math/math_funcs.h" +#include "os/os.h" +#include "servers/audio_server.h" int AudioRBResampler::get_channel_count() const { @@ -37,8 +40,11 @@ int AudioRBResampler::get_channel_count() const { return channels; } +// Linear interpolation based sample rate convertion (low quality) +// Note that AudioStreamPlaybackResampled::mix has better algorithm, +// but it wasn't obvious to integrate that with VideoPlayer template <int C> -uint32_t AudioRBResampler::_resample(int32_t *p_dest, int p_todo, int32_t p_increment) { +uint32_t AudioRBResampler::_resample(AudioFrame *p_dest, int p_todo, int32_t p_increment) { uint32_t read = offset & MIX_FRAC_MASK; @@ -47,186 +53,128 @@ uint32_t AudioRBResampler::_resample(int32_t *p_dest, int p_todo, int32_t p_incr offset = (offset + p_increment) & (((1 << (rb_bits + MIX_FRAC_BITS)) - 1)); read += p_increment; uint32_t pos = offset >> MIX_FRAC_BITS; - uint32_t frac = offset & MIX_FRAC_MASK; -#ifndef FAST_AUDIO + float frac = float(offset & MIX_FRAC_MASK) / float(MIX_FRAC_LEN); ERR_FAIL_COND_V(pos >= rb_len, 0); -#endif uint32_t pos_next = (pos + 1) & rb_mask; - //printf("rb pos %i\n",pos); // since this is a template with a known compile time value (C), conditionals go away when compiling. if (C == 1) { - int32_t v0 = rb[pos]; - int32_t v0n = rb[pos_next]; -#ifndef FAST_AUDIO - v0 += (v0n - v0) * (int32_t)frac >> MIX_FRAC_BITS; -#endif - v0 <<= 16; - p_dest[i] = v0; + float v0 = rb[pos]; + float v0n = rb[pos_next]; + v0 += (v0n - v0) * frac; + p_dest[i] = AudioFrame(v0, v0); } + if (C == 2) { - int32_t v0 = rb[(pos << 1) + 0]; - int32_t v1 = rb[(pos << 1) + 1]; - int32_t v0n = rb[(pos_next << 1) + 0]; - int32_t v1n = rb[(pos_next << 1) + 1]; - -#ifndef FAST_AUDIO - v0 += (v0n - v0) * (int32_t)frac >> MIX_FRAC_BITS; - v1 += (v1n - v1) * (int32_t)frac >> MIX_FRAC_BITS; -#endif - v0 <<= 16; - v1 <<= 16; - p_dest[(i << 1) + 0] = v0; - p_dest[(i << 1) + 1] = v1; + float v0 = rb[(pos << 1) + 0]; + float v1 = rb[(pos << 1) + 1]; + float v0n = rb[(pos_next << 1) + 0]; + float v1n = rb[(pos_next << 1) + 1]; + + v0 += (v0n - v0) * frac; + v1 += (v1n - v1) * frac; + p_dest[i] = AudioFrame(v0, v1); } + // For now, channels higher than stereo are almost ignored if (C == 4) { - int32_t v0 = rb[(pos << 2) + 0]; - int32_t v1 = rb[(pos << 2) + 1]; - int32_t v2 = rb[(pos << 2) + 2]; - int32_t v3 = rb[(pos << 2) + 3]; - int32_t v0n = rb[(pos_next << 2) + 0]; - int32_t v1n = rb[(pos_next << 2) + 1]; - int32_t v2n = rb[(pos_next << 2) + 2]; - int32_t v3n = rb[(pos_next << 2) + 3]; - -#ifndef FAST_AUDIO - v0 += (v0n - v0) * (int32_t)frac >> MIX_FRAC_BITS; - v1 += (v1n - v1) * (int32_t)frac >> MIX_FRAC_BITS; - v2 += (v2n - v2) * (int32_t)frac >> MIX_FRAC_BITS; - v3 += (v3n - v3) * (int32_t)frac >> MIX_FRAC_BITS; -#endif - v0 <<= 16; - v1 <<= 16; - v2 <<= 16; - v3 <<= 16; - p_dest[(i << 2) + 0] = v0; - p_dest[(i << 2) + 1] = v1; - p_dest[(i << 2) + 2] = v2; - p_dest[(i << 2) + 3] = v3; + float v0 = rb[(pos << 2) + 0]; + float v1 = rb[(pos << 2) + 1]; + float v2 = rb[(pos << 2) + 2]; + float v3 = rb[(pos << 2) + 3]; + float v0n = rb[(pos_next << 2) + 0]; + float v1n = rb[(pos_next << 2) + 1]; + float v2n = rb[(pos_next << 2) + 2]; + float v3n = rb[(pos_next << 2) + 3]; + + v0 += (v0n - v0) * frac; + v1 += (v1n - v1) * frac; + v2 += (v2n - v2) * frac; + v3 += (v3n - v3) * frac; + p_dest[i] = AudioFrame(v0, v1); } if (C == 6) { - int32_t v0 = rb[(pos * 6) + 0]; - int32_t v1 = rb[(pos * 6) + 1]; - int32_t v2 = rb[(pos * 6) + 2]; - int32_t v3 = rb[(pos * 6) + 3]; - int32_t v4 = rb[(pos * 6) + 4]; - int32_t v5 = rb[(pos * 6) + 5]; - int32_t v0n = rb[(pos_next * 6) + 0]; - int32_t v1n = rb[(pos_next * 6) + 1]; - int32_t v2n = rb[(pos_next * 6) + 2]; - int32_t v3n = rb[(pos_next * 6) + 3]; - int32_t v4n = rb[(pos_next * 6) + 4]; - int32_t v5n = rb[(pos_next * 6) + 5]; - -#ifndef FAST_AUDIO - v0 += (v0n - v0) * (int32_t)frac >> MIX_FRAC_BITS; - v1 += (v1n - v1) * (int32_t)frac >> MIX_FRAC_BITS; - v2 += (v2n - v2) * (int32_t)frac >> MIX_FRAC_BITS; - v3 += (v3n - v3) * (int32_t)frac >> MIX_FRAC_BITS; - v4 += (v4n - v4) * (int32_t)frac >> MIX_FRAC_BITS; - v5 += (v5n - v5) * (int32_t)frac >> MIX_FRAC_BITS; -#endif - v0 <<= 16; - v1 <<= 16; - v2 <<= 16; - v3 <<= 16; - v4 <<= 16; - v5 <<= 16; - p_dest[(i * 6) + 0] = v0; - p_dest[(i * 6) + 1] = v1; - p_dest[(i * 6) + 2] = v2; - p_dest[(i * 6) + 3] = v3; - p_dest[(i * 6) + 4] = v4; - p_dest[(i * 6) + 5] = v5; + float v0 = rb[(pos * 6) + 0]; + float v1 = rb[(pos * 6) + 1]; + float v2 = rb[(pos * 6) + 2]; + float v3 = rb[(pos * 6) + 3]; + float v4 = rb[(pos * 6) + 4]; + float v5 = rb[(pos * 6) + 5]; + float v0n = rb[(pos_next * 6) + 0]; + float v1n = rb[(pos_next * 6) + 1]; + float v2n = rb[(pos_next * 6) + 2]; + float v3n = rb[(pos_next * 6) + 3]; + float v4n = rb[(pos_next * 6) + 4]; + float v5n = rb[(pos_next * 6) + 5]; + + p_dest[i] = AudioFrame(v0, v1); } } - return read >> MIX_FRAC_BITS; //rb_read_pos=offset>>MIX_FRAC_BITS; + return read >> MIX_FRAC_BITS; //rb_read_pos = offset >> MIX_FRAC_BITS; } -bool AudioRBResampler::mix(int32_t *p_dest, int p_frames) { +bool AudioRBResampler::mix(AudioFrame *p_dest, int p_frames) { if (!rb) return false; - int write_pos_cache = rb_write_pos; - int32_t increment = (src_mix_rate * MIX_FRAC_LEN) / target_mix_rate; - - int rb_todo; - - if (write_pos_cache == rb_read_pos) { - return false; //out of buffer - - } else if (rb_read_pos < write_pos_cache) { - - rb_todo = write_pos_cache - rb_read_pos; //-1? - } else { - - rb_todo = (rb_len - rb_read_pos) + write_pos_cache; //-1? - } - - int todo = MIN(((int64_t(rb_todo) << MIX_FRAC_BITS) / increment) + 1, p_frames); + int read_space = get_reader_space(); + int target_todo = MIN(get_num_of_ready_frames(), p_frames); { - - int read = 0; + int src_read = 0; switch (channels) { - case 1: read = _resample<1>(p_dest, todo, increment); break; - case 2: read = _resample<2>(p_dest, todo, increment); break; - case 4: read = _resample<4>(p_dest, todo, increment); break; - case 6: read = _resample<6>(p_dest, todo, increment); break; + case 1: src_read = _resample<1>(p_dest, target_todo, increment); break; + case 2: src_read = _resample<2>(p_dest, target_todo, increment); break; + case 4: src_read = _resample<4>(p_dest, target_todo, increment); break; + case 6: src_read = _resample<6>(p_dest, target_todo, increment); break; } - //end of stream, fadeout - int remaining = p_frames - todo; - if (remaining && todo > 0) { - - //print_line("fadeout"); - for (uint32_t c = 0; c < channels; c++) { + if (src_read > read_space) + src_read = read_space; - for (int i = 0; i < todo; i++) { + rb_read_pos = (rb_read_pos + src_read) & rb_mask; - int32_t samp = p_dest[i * channels + c] >> 8; - uint32_t mul = (todo - i) * 256 / todo; - //print_line("mul: "+itos(i)+" "+itos(mul)); - p_dest[i * channels + c] = samp * mul; - } + // Create fadeout effect for the end of stream (note that it can be because of slow writer) + if (p_frames - target_todo > 0) { + for (int i = 0; i < target_todo; i++) { + p_dest[i] = p_dest[i] * float(target_todo - i) / float(target_todo); } } - //zero out what remains there to avoid glitches - for (uint32_t i = todo * channels; i < int(p_frames) * channels; i++) { - - p_dest[i] = 0; + // Fill zeros (silence) for the rest of frames + for (uint32_t i = target_todo; i < p_frames; i++) { + p_dest[i] = AudioFrame(0, 0); } - - if (read > rb_todo) - read = rb_todo; - - rb_read_pos = (rb_read_pos + read) & rb_mask; } return true; } +int AudioRBResampler::get_num_of_ready_frames() { + if (!is_ready()) + return 0; + int32_t increment = (src_mix_rate * MIX_FRAC_LEN) / target_mix_rate; + int read_space = get_reader_space(); + return (int64_t(read_space) << MIX_FRAC_BITS) / increment; +} + Error AudioRBResampler::setup(int p_channels, int p_src_mix_rate, int p_target_mix_rate, int p_buffer_msec, int p_minbuff_needed) { ERR_FAIL_COND_V(p_channels != 1 && p_channels != 2 && p_channels != 4 && p_channels != 6, ERR_INVALID_PARAMETER); - //float buffering_sec = int(GLOBAL_DEF("audio/stream_buffering_ms",500))/1000.0; int desired_rb_bits = nearest_shift(MAX((p_buffer_msec / 1000.0) * p_src_mix_rate, p_minbuff_needed)); bool recreate = !rb; if (rb && (uint32_t(desired_rb_bits) != rb_bits || channels != uint32_t(p_channels))) { - //recreate memdelete_arr(rb); memdelete_arr(read_buf); @@ -239,8 +187,8 @@ Error AudioRBResampler::setup(int p_channels, int p_src_mix_rate, int p_target_m rb_bits = desired_rb_bits; rb_len = (1 << rb_bits); rb_mask = rb_len - 1; - rb = memnew_arr(int16_t, rb_len * p_channels); - read_buf = memnew_arr(int16_t, rb_len * p_channels); + rb = memnew_arr(float, rb_len *p_channels); + read_buf = memnew_arr(float, rb_len *p_channels); } src_mix_rate = p_src_mix_rate; diff --git a/servers/audio/audio_rb_resampler.h b/servers/audio/audio_rb_resampler.h index bc1f924ab5..08c7a5a668 100644 --- a/servers/audio/audio_rb_resampler.h +++ b/servers/audio/audio_rb_resampler.h @@ -31,6 +31,7 @@ #define AUDIO_RB_RESAMPLER_H #include "os/memory.h" +#include "servers/audio_server.h" #include "typedefs.h" struct AudioRBResampler { @@ -53,11 +54,11 @@ struct AudioRBResampler { MIX_FRAC_MASK = MIX_FRAC_LEN - 1, }; - int16_t *read_buf; - int16_t *rb; + float *read_buf; + float *rb; template <int C> - uint32_t _resample(int32_t *p_dest, int p_todo, int32_t p_increment); + uint32_t _resample(AudioFrame *p_dest, int p_todo, int32_t p_increment); public: _FORCE_INLINE_ void flush() { @@ -71,33 +72,48 @@ public: } _FORCE_INLINE_ int get_total() const { - return rb_len - 1; } - _FORCE_INLINE_ int get_todo() const { //return amount of frames to mix - - int todo; - int read_pos_cache = rb_read_pos; + _FORCE_INLINE_ int get_writer_space() const { + int space, r, w; - if (read_pos_cache == rb_write_pos) { - todo = rb_len - 1; - } else if (read_pos_cache > rb_write_pos) { + r = rb_read_pos; + w = rb_write_pos; - todo = read_pos_cache - rb_write_pos - 1; + if (r == w) { + space = rb_len - 1; + } else if (w < r) { + space = r - w - 1; } else { + space = (rb_len - r) + w - 1; + } + + return space; + } + + _FORCE_INLINE_ int get_reader_space() const { + int space, r, w; - todo = (rb_len - rb_write_pos) + read_pos_cache - 1; + r = rb_read_pos; + w = rb_write_pos; + + if (r == w) { + space = 0; + } else if (w < r) { + space = rb_len - r + w; + } else { + space = w - r; } - return todo; + return space; } _FORCE_INLINE_ bool has_data() const { return rb && rb_read_pos != rb_write_pos; } - _FORCE_INLINE_ int16_t *get_write_buffer() { return read_buf; } + _FORCE_INLINE_ float *get_write_buffer() { return read_buf; } _FORCE_INLINE_ void write(uint32_t p_frames) { ERR_FAIL_COND(p_frames >= rb_len); @@ -151,7 +167,8 @@ public: Error setup(int p_channels, int p_src_mix_rate, int p_target_mix_rate, int p_buffer_msec, int p_minbuff_needed = -1); void clear(); - bool mix(int32_t *p_dest, int p_frames); + bool mix(AudioFrame *p_dest, int p_frames); + int get_num_of_ready_frames(); AudioRBResampler(); ~AudioRBResampler(); diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index 697abead68..6a10d7539d 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -876,6 +876,8 @@ void AudioServer::init() { #ifdef TOOLS_ENABLED set_edited(false); //avoid editors from thinking this was edited #endif + + GLOBAL_DEF("audio/video_delay_compensation_ms", 0); } void AudioServer::load_default_bus_layout() { diff --git a/thirdparty/libsimplewebm/OpusVorbisDecoder.cpp b/thirdparty/libsimplewebm/OpusVorbisDecoder.cpp index 06447aca57..c9e71eb733 100644 --- a/thirdparty/libsimplewebm/OpusVorbisDecoder.cpp +++ b/thirdparty/libsimplewebm/OpusVorbisDecoder.cpp @@ -122,6 +122,43 @@ bool OpusVorbisDecoder::getPCMS16(WebMFrame &frame, short *buffer, int &numOutSa return false; } +bool OpusVorbisDecoder::getPCMF(WebMFrame &frame, float *buffer, int &numOutSamples) { + if (m_vorbis) { + m_vorbis->op.packet = frame.buffer; + m_vorbis->op.bytes = frame.bufferSize; + + if (vorbis_synthesis(&m_vorbis->block, &m_vorbis->op)) + return false; + if (vorbis_synthesis_blockin(&m_vorbis->dspState, &m_vorbis->block)) + return false; + + const int maxSamples = getBufferSamples(); + int samplesCount, count = 0; + float **pcm; + while ((samplesCount = vorbis_synthesis_pcmout(&m_vorbis->dspState, &pcm))) { + const int toConvert = samplesCount <= maxSamples ? samplesCount : maxSamples; + for (int c = 0; c < m_channels; ++c) { + float *samples = pcm[c]; + for (int i = 0, j = c; i < toConvert; ++i, j += m_channels) { + buffer[count + j] = samples[i]; + } + } + vorbis_synthesis_read(&m_vorbis->dspState, toConvert); + count += toConvert; + } + + numOutSamples = count; + return true; + } else if (m_opus) { + const int samples = opus_decode_float(m_opus, frame.buffer, frame.bufferSize, buffer, m_numSamples, 0); + if (samples >= 0) { + numOutSamples = samples; + return true; + } + } + return false; +} + bool OpusVorbisDecoder::openVorbis(const WebMDemuxer &demuxer) { size_t extradataSize = 0; diff --git a/thirdparty/libsimplewebm/OpusVorbisDecoder.hpp b/thirdparty/libsimplewebm/OpusVorbisDecoder.hpp index bcdca731ee..b7619d6a25 100644 --- a/thirdparty/libsimplewebm/OpusVorbisDecoder.hpp +++ b/thirdparty/libsimplewebm/OpusVorbisDecoder.hpp @@ -44,7 +44,7 @@ public: { return m_numSamples; } - + bool getPCMF(WebMFrame &frame, float *buffer, int &numOutSamples); bool getPCMS16(WebMFrame &frame, short *buffer, int &numOutSamples); private: |