summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml37
-rw-r--r--editor/import_dock.cpp63
-rw-r--r--editor/import_dock.h1
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.cpp8
-rw-r--r--editor/plugins/script_editor_plugin.cpp325
-rw-r--r--editor/plugins/script_editor_plugin.h14
-rw-r--r--editor/plugins/script_text_editor.cpp12
-rw-r--r--editor/plugins/shader_editor_plugin.cpp399
-rw-r--r--editor/plugins/shader_editor_plugin.h18
-rw-r--r--editor/plugins/spatial_editor_plugin.cpp3
-rwxr-xr-xmisc/travis/android-tools-linux.sh107
-rwxr-xr-xmisc/travis/android-tools-osx.sh107
-rwxr-xr-xmisc/travis/ccache-osx.sh43
-rwxr-xr-xmisc/travis/scons-local-osx.sh18
-rw-r--r--modules/gdnative/SCsub6
-rw-r--r--modules/gdscript/gd_editor.cpp2
-rw-r--r--modules/ogg/config.py3
-rw-r--r--modules/opus/SCsub11
-rw-r--r--modules/opus/config.py3
-rw-r--r--modules/opus/stub/register_types.cpp36
-rw-r--r--modules/opus/stub/register_types.h31
-rw-r--r--modules/theora/config.py3
-rw-r--r--modules/theora/register_types.cpp13
-rw-r--r--modules/theora/resource_importer_theora.cpp89
-rw-r--r--modules/theora/resource_importer_theora.h57
-rw-r--r--modules/theora/video_stream_theora.cpp46
-rw-r--r--modules/theora/video_stream_theora.h16
-rw-r--r--modules/vorbis/SCsub10
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.cpp4
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.h2
-rw-r--r--modules/vorbis/config.py3
-rw-r--r--modules/vorbis/stub/register_types.cpp36
-rw-r--r--modules/vorbis/stub/register_types.h31
-rw-r--r--modules/webm/config.py3
-rw-r--r--modules/webm/libvpx/SCsub4
-rw-r--r--modules/webm/register_types.cpp13
-rw-r--r--modules/webm/resource_importer_webm.cpp95
-rw-r--r--modules/webm/resource_importer_webm.h55
-rw-r--r--modules/webm/video_stream_webm.cpp153
-rw-r--r--modules/webm/video_stream_webm.h23
-rw-r--r--platform/android/detect.py11
-rw-r--r--platform/iphone/detect.py21
-rw-r--r--platform/osx/detect.py11
-rw-r--r--scene/animation/animation_player.cpp9
-rw-r--r--scene/gui/scroll_container.cpp5
-rw-r--r--scene/gui/text_edit.cpp7
-rw-r--r--scene/gui/text_edit.h1
-rw-r--r--scene/gui/video_player.cpp144
-rw-r--r--scene/gui/video_player.h22
-rwxr-xr-xscene/main/node.cpp4
-rw-r--r--scene/resources/primitive_meshes.cpp38
-rw-r--r--scene/resources/primitive_meshes.h5
-rw-r--r--scene/resources/video_stream.h4
-rw-r--r--servers/audio/audio_rb_resampler.cpp210
-rw-r--r--servers/audio/audio_rb_resampler.h49
-rw-r--r--servers/audio_server.cpp2
-rw-r--r--thirdparty/libsimplewebm/OpusVorbisDecoder.cpp37
-rw-r--r--thirdparty/libsimplewebm/OpusVorbisDecoder.hpp2
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, &current_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: