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: |