diff options
29 files changed, 858 insertions, 182 deletions
diff --git a/core/error_macros.h b/core/error_macros.h index 69874e280b..65802de9d2 100644 --- a/core/error_macros.h +++ b/core/error_macros.h @@ -140,6 +140,16 @@ extern bool _err_error_exists; _err_error_exists = false; \ } while (0); // (*) +#define ERR_FAIL_INDEX_MSG(m_index, m_size, m_msg) \ + do { \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ + return; \ + } \ + _err_error_exists = false; \ + } while (0); // (*) + /** An index has failed if m_index<0 or m_index >=m_size, the function exits. * This function returns an error value, if returning Error, please select the most * appropriate error condition from error_macros.h @@ -154,6 +164,16 @@ extern bool _err_error_exists; _err_error_exists = false; \ } while (0); // (*) +#define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \ + do { \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ + return m_retval; \ + } \ + _err_error_exists = false; \ + } while (0); // (*) + /** An index has failed if m_index >=m_size, the function exits. * This function returns an error value, if returning Error, please select the most * appropriate error condition from error_macros.h @@ -168,6 +188,16 @@ extern bool _err_error_exists; _err_error_exists = false; \ } while (0); // (*) +#define ERR_FAIL_UNSIGNED_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \ + do { \ + if (unlikely((m_index) >= (m_size))) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ + return m_retval; \ + } \ + _err_error_exists = false; \ + } while (0); // (*) + /** Use this one if there is no sensible fallback, that is, the error is unrecoverable. * We'll return a null reference and try to keep running. */ @@ -179,6 +209,15 @@ extern bool _err_error_exists; } \ } while (0); // (*) +#define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg) \ + do { \ + if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), true); \ + GENERATE_TRAP \ + } \ + } while (0); // (*) + /** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert(). * the function will exit. */ @@ -192,6 +231,16 @@ extern bool _err_error_exists; _err_error_exists = false; \ } +#define ERR_FAIL_NULL_MSG(m_param, m_msg) \ + { \ + if (unlikely(!m_param)) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter ' " _STR(m_param) " ' is null."); \ + return; \ + } \ + _err_error_exists = false; \ + } + #define ERR_FAIL_NULL_V(m_param, m_retval) \ { \ if (unlikely(!m_param)) { \ @@ -201,6 +250,16 @@ extern bool _err_error_exists; _err_error_exists = false; \ } +#define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg) \ + { \ + if (unlikely(!m_param)) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter ' " _STR(m_param) " ' is null."); \ + return m_retval; \ + } \ + _err_error_exists = false; \ + } + /** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert(). * the function will exit. */ @@ -214,6 +273,16 @@ extern bool _err_error_exists; _err_error_exists = false; \ } +#define ERR_FAIL_COND_MSG(m_cond, m_msg) \ + { \ + if (unlikely(m_cond)) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true."); \ + return; \ + } \ + _err_error_exists = false; \ + } + /** Use this one if there is no sensible fallback, that is, the error is unrecoverable. */ @@ -225,6 +294,15 @@ extern bool _err_error_exists; } \ } +#define CRASH_COND_MSG(m_cond, m_msg) \ + { \ + if (unlikely(m_cond)) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition ' " _STR(m_cond) " ' is true."); \ + GENERATE_TRAP \ + } \ + } + /** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert(). * the function will exit. * This function returns an error value, if returning Error, please select the most @@ -240,6 +318,16 @@ extern bool _err_error_exists; _err_error_exists = false; \ } +#define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \ + { \ + if (unlikely(m_cond)) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true. returned: " _STR(m_retval)); \ + return m_retval; \ + } \ + _err_error_exists = false; \ + } + /** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert(). * the loop will skip to the next iteration. */ @@ -253,6 +341,16 @@ extern bool _err_error_exists; _err_error_exists = false; \ } +#define ERR_CONTINUE_MSG(m_cond, m_msg) \ + { \ + if (unlikely(m_cond)) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true. Continuing..:"); \ + continue; \ + } \ + _err_error_exists = false; \ + } + /** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert(). * the loop will break */ @@ -266,6 +364,16 @@ extern bool _err_error_exists; _err_error_exists = false; \ } +#define ERR_BREAK_MSG(m_cond, m_msg) \ + { \ + if (unlikely(m_cond)) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true. Breaking..:"); \ + break; \ + } \ + _err_error_exists = false; \ + } + /** Print an error string and return */ @@ -276,6 +384,12 @@ extern bool _err_error_exists; return; \ } +#define ERR_FAIL_MSG(m_msg) \ + { \ + ERR_EXPLAIN(m_msg); \ + ERR_FAIL(); \ + } + /** Print an error string and return with value */ @@ -286,6 +400,12 @@ extern bool _err_error_exists; return m_value; \ } +#define ERR_FAIL_V_MSG(m_value, m_msg) \ + { \ + ERR_EXPLAIN(m_msg); \ + ERR_FAIL_V(m_value); \ + } + /** Use this one if there is no sensible fallback, that is, the error is unrecoverable. */ @@ -295,6 +415,12 @@ extern bool _err_error_exists; GENERATE_TRAP \ } +#define CRASH_NOW_MSG(m_msg) \ + { \ + ERR_EXPLAIN(m_msg); \ + CRASH_NOW(); \ + } + /** Print an error string. */ @@ -355,4 +481,15 @@ extern bool _err_error_exists; } \ } +#define WARN_DEPRECATED_MSG(m_msg) \ + { \ + static volatile bool warning_shown = false; \ + if (!warning_shown) { \ + ERR_EXPLAIN(m_msg); \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future", ERR_HANDLER_WARNING); \ + _err_error_exists = false; \ + warning_shown = true; \ + } \ + } + #endif diff --git a/core/func_ref.cpp b/core/func_ref.cpp index 3d03137d09..66ef27f6b9 100644 --- a/core/func_ref.cpp +++ b/core/func_ref.cpp @@ -46,6 +46,17 @@ Variant FuncRef::call_func(const Variant **p_args, int p_argcount, Variant::Call return obj->call(function, p_args, p_argcount, r_error); } +Variant FuncRef::call_funcv(const Array &p_args) { + + ERR_FAIL_COND_V(id == 0, Variant()); + + Object *obj = ObjectDB::get_instance(id); + + ERR_FAIL_COND_V(!obj, Variant()); + + return obj->callv(function, p_args); +} + void FuncRef::set_instance(Object *p_obj) { ERR_FAIL_NULL(p_obj); @@ -77,6 +88,8 @@ void FuncRef::_bind_methods() { ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "call_func", &FuncRef::call_func, mi, defargs); } + ClassDB::bind_method(D_METHOD("call_funcv", "arg_array"), &FuncRef::call_funcv); + ClassDB::bind_method(D_METHOD("set_instance", "instance"), &FuncRef::set_instance); ClassDB::bind_method(D_METHOD("set_function", "name"), &FuncRef::set_function); ClassDB::bind_method(D_METHOD("is_valid"), &FuncRef::is_valid); diff --git a/core/func_ref.h b/core/func_ref.h index a143b58bf0..af0bf63203 100644 --- a/core/func_ref.h +++ b/core/func_ref.h @@ -44,6 +44,7 @@ protected: public: Variant call_func(const Variant **p_args, int p_argcount, Variant::CallError &r_error); + Variant call_funcv(const Array &p_args); void set_instance(Object *p_obj); void set_function(const StringName &p_func); bool is_valid() const; diff --git a/core/image.cpp b/core/image.cpp index 10778eced6..5ce744f709 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -83,6 +83,7 @@ const char *Image::format_names[Image::FORMAT_MAX] = { }; SavePNGFunc Image::save_png_func = NULL; +SaveEXRFunc Image::save_exr_func = NULL; void Image::_put_pixelb(int p_x, int p_y, uint32_t p_pixelsize, uint8_t *p_data, const uint8_t *p_pixel) { @@ -1917,6 +1918,14 @@ Error Image::save_png(const String &p_path) const { return save_png_func(p_path, Ref<Image>((Image *)this)); } +Error Image::save_exr(const String &p_path, bool p_grayscale) const { + + if (save_exr_func == NULL) + return ERR_UNAVAILABLE; + + return save_exr_func(p_path, Ref<Image>((Image *)this), p_grayscale); +} + int Image::get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps) { int mm; @@ -2746,6 +2755,7 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("load", "path"), &Image::load); ClassDB::bind_method(D_METHOD("save_png", "path"), &Image::save_png); + ClassDB::bind_method(D_METHOD("save_exr", "path", "grayscale"), &Image::save_exr, DEFVAL(false)); ClassDB::bind_method(D_METHOD("detect_alpha"), &Image::detect_alpha); ClassDB::bind_method(D_METHOD("is_invisible"), &Image::is_invisible); diff --git a/core/image.h b/core/image.h index cc796789cd..d17571399d 100644 --- a/core/image.h +++ b/core/image.h @@ -49,11 +49,14 @@ class Image; typedef Error (*SavePNGFunc)(const String &p_path, const Ref<Image> &p_img); typedef Ref<Image> (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size); +typedef Error (*SaveEXRFunc)(const String &p_path, const Ref<Image> &p_img, bool p_grayscale); + class Image : public Resource { GDCLASS(Image, Resource); public: static SavePNGFunc save_png_func; + static SaveEXRFunc save_exr_func; enum { MAX_WIDTH = 16384, // force a limit somehow @@ -258,6 +261,7 @@ public: Error load(const String &p_path); Error save_png(const String &p_path) const; + Error save_exr(const String &p_path, bool p_grayscale) const; /** * create an empty image diff --git a/core/object.h b/core/object.h index dce1cc74ae..15c3ab94c5 100644 --- a/core/object.h +++ b/core/object.h @@ -794,8 +794,13 @@ public: static int get_object_count(); _FORCE_INLINE_ static bool instance_validate(Object *p_ptr) { + rw_lock->read_lock(); - return instance_checks.has(p_ptr); + bool exists = instance_checks.has(p_ptr); + + rw_lock->read_unlock(); + + return exists; } }; diff --git a/core/translation.cpp b/core/translation.cpp index 0b55badc61..a3ff971f45 100644 --- a/core/translation.cpp +++ b/core/translation.cpp @@ -1044,6 +1044,13 @@ StringName TranslationServer::translate(const StringName &p_message) const { if (!enabled) return p_message; + // Locale can be of the form 'll_CC', i.e. language code and regional code, + // e.g. 'en_US', 'en_GB', etc. It might also be simply 'll', e.g. 'en'. + // To find the relevant translation, we look for those with locale starting + // with the language code, and then if any is an exact match for the long + // form. If not found, we fall back to a near match (another locale with + // same language code). + StringName res; bool near_match = false; const CharType *lptr = &locale[0]; @@ -1053,13 +1060,11 @@ StringName TranslationServer::translate(const StringName &p_message) const { const Ref<Translation> &t = E->get(); String l = t->get_locale(); if (lptr[0] != l[0] || lptr[1] != l[1]) - continue; // locale not match - - //near match - bool match = (l != locale); + continue; // Language code does not match. - if (near_match && !match) - continue; //only near-match once + bool exact_match = (l == locale); + if (!exact_match && near_match) + continue; // Only near-match once, but keep looking for exact matches. StringName r = t->get_message(p_message); @@ -1068,43 +1073,38 @@ StringName TranslationServer::translate(const StringName &p_message) const { res = r; - if (match) + if (exact_match) break; else near_match = true; } - if (!res) { - //try again with fallback - if (fallback.length() >= 2) { - - const CharType *fptr = &fallback[0]; - near_match = false; - for (const Set<Ref<Translation> >::Element *E = translations.front(); E; E = E->next()) { + if (!res && fallback.length() >= 2) { + // Try again with the fallback locale. + const CharType *fptr = &fallback[0]; + near_match = false; + for (const Set<Ref<Translation> >::Element *E = translations.front(); E; E = E->next()) { - const Ref<Translation> &t = E->get(); - String l = t->get_locale(); - if (fptr[0] != l[0] || fptr[1] != l[1]) - continue; // locale not match + const Ref<Translation> &t = E->get(); + String l = t->get_locale(); + if (fptr[0] != l[0] || fptr[1] != l[1]) + continue; // Language code does not match. - //near match - bool match = (l != fallback); + bool exact_match = (l == fallback); + if (!exact_match && near_match) + continue; // Only near-match once, but keep looking for exact matches. - if (near_match && !match) - continue; //only near-match once + StringName r = t->get_message(p_message); - StringName r = t->get_message(p_message); + if (!r) + continue; - if (!r) - continue; + res = r; - res = r; - - if (match) - break; - else - near_match = true; - } + if (exact_match) + break; + else + near_match = true; } } diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml index cc3d5a1139..86c1002666 100644 --- a/doc/classes/Environment.xml +++ b/doc/classes/Environment.xml @@ -157,11 +157,11 @@ <member name="fog_height_enabled" type="bool" setter="set_fog_height_enabled" getter="is_fog_height_enabled" default="false"> Enables the fog height. </member> - <member name="fog_height_max" type="float" setter="set_fog_height_max" getter="get_fog_height_max" default="100.0"> - Maximum height of fog. + <member name="fog_height_max" type="float" setter="set_fog_height_max" getter="get_fog_height_max" default="0.0"> + The Y coordinate where the height fog will be the most intense. If this value is greater than [member fog_height_min], fog will be displayed from bottom to top. Otherwise, it will be displayed from top to bottom. </member> - <member name="fog_height_min" type="float" setter="set_fog_height_min" getter="get_fog_height_min" default="0.0"> - Minimum height of fog. + <member name="fog_height_min" type="float" setter="set_fog_height_min" getter="get_fog_height_min" default="10.0"> + The Y coordinate where the height fog will be the least intense. If this value is greater than [member fog_height_max], fog will be displayed from top to bottom. Otherwise, it will be displayed from bottom to top. </member> <member name="fog_sun_amount" type="float" setter="set_fog_sun_amount" getter="get_fog_sun_amount" default="0.0"> Amount of sun that affects the fog rendering. diff --git a/doc/classes/FuncRef.xml b/doc/classes/FuncRef.xml index e35d7a68c5..9803ae0838 100644 --- a/doc/classes/FuncRef.xml +++ b/doc/classes/FuncRef.xml @@ -17,10 +17,20 @@ Calls the referenced function previously set by [method set_function] or [method @GDScript.funcref]. </description> </method> + <method name="call_funcv"> + <return type="Variant"> + </return> + <argument index="0" name="arg_array" type="Array"> + </argument> + <description> + Calls the referenced function previously set by [method set_function] or [method @GDScript.funcref]. Contrarily to [method call_func], this method does not support a variable number of arguments but expects all parameters to be passed via a single [Array]. + </description> + </method> <method name="is_valid" qualifiers="const"> <return type="bool"> </return> <description> + Returns whether the object still exists and has the function assigned. </description> </method> <method name="set_function"> diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index 8cd69ba0da..a4df0d5c19 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -415,6 +415,17 @@ Saves the image as a PNG file to [code]path[/code]. </description> </method> + <method name="save_exr" qualifiers="const"> + <return type="int" enum="Error"> + </return> + <argument index="0" name="path" type="String"> + </argument> + <argument index="1" name="grayscale" type="bool" default="false"> + </argument> + <description> + Saves the image as an EXR file to [code]path[/code]. If grayscale is true and the image has only one channel, it will be saved explicitely as monochrome rather than one red channel. This function will return [constant ERR_UNAVAILABLE] if Godot was compiled without the TinyEXR module. + </description> + </method> <method name="set_pixel"> <return type="void"> </return> diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml index b87e912b45..4b77197e29 100644 --- a/doc/classes/Object.xml +++ b/doc/classes/Object.xml @@ -112,7 +112,7 @@ <argument index="1" name="arg_array" type="Array"> </argument> <description> - Calls the [code]method[/code] on the object and returns the result. Contrarily to [method call], this method does not support a variable number of arguments but expected all parameters passed via a single [Array]. + Calls the [code]method[/code] on the object and returns the result. Contrarily to [method call], this method does not support a variable number of arguments but expects all parameters to be via a single [Array]. [codeblock] callv("set", [ "position", Vector2(42.0, 0.0) ]) [/codeblock] diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml index c2b7901c05..62a7147e08 100644 --- a/doc/classes/Tree.xml +++ b/doc/classes/Tree.xml @@ -16,6 +16,7 @@ var subchild1 = tree.create_item(child1) subchild1.set_text(0, "Subchild1") [/codeblock] + To iterate over all the [TreeItem] objects in a [Tree] object, use [method TreeItem.get_next] and [method TreeItem.get_children] after getting the root through [get_root]. </description> <tutorials> </tutorials> diff --git a/drivers/gles2/rasterizer_scene_gles2.h b/drivers/gles2/rasterizer_scene_gles2.h index f60ca9675e..69a2295fc1 100644 --- a/drivers/gles2/rasterizer_scene_gles2.h +++ b/drivers/gles2/rasterizer_scene_gles2.h @@ -398,8 +398,8 @@ public: fog_transmit_enabled(true), fog_transmit_curve(1), fog_height_enabled(false), - fog_height_min(0), - fog_height_max(100), + fog_height_min(10), + fog_height_max(0), fog_height_curve(1) { } }; diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index b5e64b6162..3d09adcfeb 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -527,8 +527,8 @@ public: fog_transmit_enabled(true), fog_transmit_curve(1), fog_height_enabled(false), - fog_height_min(0), - fog_height_max(100), + fog_height_min(10), + fog_height_max(0), fog_height_curve(1) { } }; diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 4eceb09792..97af0d08ea 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -113,11 +113,11 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ffffff", "#000000"); // white ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#b4b4b4", "#000000"); // script darker color - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#cea4f1", "#bb6dff"); // animation - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#fc9c9c", "#ff5f5f"); // spatial - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#a5b7f3", "#6d90ff"); // 2d - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#708cea", "#0843ff"); // 2d dark - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#a5efac", "#29d739"); // control + ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#cea4f1", "#a85de9"); // animation + ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#fc9c9c", "#cd3838"); // spatial + ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#a5b7f3", "#3d64dd"); // 2d + ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#708cea", "#1a3eac"); // 2d dark + ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#a5efac", "#2aa235"); // control // rainbow ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ff7070", "#ff2929"); // red @@ -140,7 +140,13 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#84ffb1", "#00db50"); // add (green) ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#84c2ff", "#5caeff"); // selection (blue) - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ea686c", "#e3383d"); // key xform (red) + // Animation editor tracks + // The property track icon color is set by the common icon color + ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ea9568", "#bd5e2c"); // 3D Transform track + ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#66f376", "#16a827"); // Call Method track + ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#5792f6", "#236be6"); // Bezier Curve track + ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#eae668", "#aea923"); // Audio Playback track + ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#b76ef0", "#9853ce"); // Animation Playback track ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#69ecbd", "#25e3a0"); // VS variant ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#8da6f0", "#6d8eeb"); // VS bool diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index e1ab5c62a7..8665467f2d 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -524,6 +524,7 @@ FindInFilesPanel::FindInFilesPanel() { _progress_bar = memnew(ProgressBar); _progress_bar->set_h_size_flags(SIZE_EXPAND_FILL); + _progress_bar->set_v_size_flags(SIZE_SHRINK_CENTER); hbc->add_child(_progress_bar); set_progress_visible(false); diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 31b11d8bea..b5231ce468 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -333,18 +333,14 @@ void EditorAssetLibraryItemDownload::_http_download_completed(int p_status, int switch (p_status) { - case HTTPRequest::RESULT_CANT_RESOLVE: { - error_text = TTR("Can't resolve hostname:") + " " + host; - status->set_text(TTR("Can't resolve.")); - } break; - case HTTPRequest::RESULT_BODY_SIZE_LIMIT_EXCEEDED: + case HTTPRequest::RESULT_CHUNKED_BODY_SIZE_MISMATCH: case HTTPRequest::RESULT_CONNECTION_ERROR: - case HTTPRequest::RESULT_CHUNKED_BODY_SIZE_MISMATCH: { + case HTTPRequest::RESULT_BODY_SIZE_LIMIT_EXCEEDED: { error_text = TTR("Connection error, please try again."); status->set_text(TTR("Can't connect.")); } break; - case HTTPRequest::RESULT_SSL_HANDSHAKE_ERROR: - case HTTPRequest::RESULT_CANT_CONNECT: { + case HTTPRequest::RESULT_CANT_CONNECT: + case HTTPRequest::RESULT_SSL_HANDSHAKE_ERROR: { error_text = TTR("Can't connect to host:") + " " + host; status->set_text(TTR("Can't connect.")); } break; @@ -352,14 +348,27 @@ void EditorAssetLibraryItemDownload::_http_download_completed(int p_status, int error_text = TTR("No response from host:") + " " + host; status->set_text(TTR("No response.")); } break; + case HTTPRequest::RESULT_CANT_RESOLVE: { + error_text = TTR("Can't resolve hostname:") + " " + host; + status->set_text(TTR("Can't resolve.")); + } break; case HTTPRequest::RESULT_REQUEST_FAILED: { error_text = TTR("Request failed, return code:") + " " + itos(p_code); status->set_text(TTR("Request Failed.")); } break; + case HTTPRequest::RESULT_DOWNLOAD_FILE_CANT_OPEN: + case HTTPRequest::RESULT_DOWNLOAD_FILE_WRITE_ERROR: { + error_text = TTR("Cannot save response to") + " " + download->get_download_file(); + status->set_text(TTR("Write error.")); + } break; case HTTPRequest::RESULT_REDIRECT_LIMIT_REACHED: { error_text = TTR("Request failed, too many redirects"); status->set_text(TTR("Redirect Loop.")); } break; + case HTTPRequest::RESULT_TIMEOUT: { + error_text = TTR("Request failed, timeout"); + status->set_text(TTR("Timeout.")); + } break; default: { if (p_code != 200) { error_text = TTR("Request failed, return code:") + " " + itos(p_code); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 02d4b9d1d7..becf60bf28 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -1420,15 +1420,25 @@ void ScriptEditor::_notification(int p_what) { } EditorSettings::get_singleton()->connect("settings_changed", this, "_editor_settings_changed"); + FALLTHROUGH; + } + case NOTIFICATION_THEME_CHANGED: { + help_search->set_icon(get_icon("HelpSearch", "EditorIcons")); site_search->set_icon(get_icon("Instance", "EditorIcons")); request_docs->set_icon(get_icon("Issue", "EditorIcons")); script_forward->set_icon(get_icon("Forward", "EditorIcons")); script_back->set_icon(get_icon("Back", "EditorIcons")); + members_overview_alphabeta_sort_button->set_icon(get_icon("Sort", "EditorIcons")); + filter_scripts->set_right_icon(get_icon("Search", "EditorIcons")); filter_methods->set_right_icon(get_icon("Search", "EditorIcons")); + + filename->add_style_override("normal", editor->get_gui_base()->get_stylebox("normal", "LineEdit")); + + recent_scripts->set_as_minsize(); } break; case NOTIFICATION_READY: { @@ -1451,20 +1461,6 @@ void ScriptEditor::_notification(int p_what) { _update_modified_scripts_for_external_editor(); } break; - case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - - help_search->set_icon(get_icon("HelpSearch", "EditorIcons")); - site_search->set_icon(get_icon("Instance", "EditorIcons")); - - script_forward->set_icon(get_icon("Forward", "EditorIcons")); - script_back->set_icon(get_icon("Back", "EditorIcons")); - - members_overview_alphabeta_sort_button->set_icon(get_icon("Sort", "EditorIcons")); - filename->add_style_override("normal", editor->get_gui_base()->get_stylebox("normal", "LineEdit")); - - recent_scripts->set_as_minsize(); - } break; - case CanvasItem::NOTIFICATION_VISIBILITY_CHANGED: { if (is_visible()) { @@ -2989,35 +2985,40 @@ void ScriptEditor::_on_find_in_files_requested(String text) { void ScriptEditor::_on_find_in_files_result_selected(String fpath, int line_number, int begin, int end) { - RES res = ResourceLoader::load(fpath); - if (fpath.get_extension() == "shader") { - ShaderEditorPlugin *shader_editor = Object::cast_to<ShaderEditorPlugin>(EditorNode::get_singleton()->get_editor_data().get_editor("Shader")); - shader_editor->edit(res.ptr()); - shader_editor->make_visible(true); - shader_editor->get_shader_editor()->goto_line_selection(line_number - 1, begin, end); - } else { - Ref<Script> script = res; - if (script.is_valid()) { - edit(script); - - ScriptTextEditor *ste = Object::cast_to<ScriptTextEditor>(_get_current_editor()); - if (ste) { - ste->goto_line_selection(line_number - 1, begin, end); - } - } else { //if file is not valid script, load as text file + if (ResourceLoader::exists(fpath)) { + RES res = ResourceLoader::load(fpath); - Error err; - Ref<TextFile> text_file = _load_text_file(fpath, &err); - if (text_file.is_valid()) { - edit(text_file); + if (fpath.get_extension() == "shader") { + ShaderEditorPlugin *shader_editor = Object::cast_to<ShaderEditorPlugin>(EditorNode::get_singleton()->get_editor_data().get_editor("Shader")); + shader_editor->edit(res.ptr()); + shader_editor->make_visible(true); + shader_editor->get_shader_editor()->goto_line_selection(line_number - 1, begin, end); + return; + } else { + Ref<Script> script = res; + if (script.is_valid()) { + edit(script); - TextEditor *te = Object::cast_to<TextEditor>(_get_current_editor()); - if (te) { - te->goto_line_selection(line_number - 1, begin, end); + ScriptTextEditor *ste = Object::cast_to<ScriptTextEditor>(_get_current_editor()); + if (ste) { + ste->goto_line_selection(line_number - 1, begin, end); } + return; } } } + + // If the file is not a valid resource/script, load it as a text file. + Error err; + Ref<TextFile> text_file = _load_text_file(fpath, &err); + if (text_file.is_valid()) { + edit(text_file); + + TextEditor *te = Object::cast_to<TextEditor>(_get_current_editor()); + if (te) { + te->goto_line_selection(line_number - 1, begin, end); + } + } } void ScriptEditor::_start_find_in_files(bool with_replace) { diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 07303da2ff..cc323c7e0a 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -1572,7 +1572,9 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { if (has_color) { String line = tx->get_line(row); - color_line = row; + color_position.x = row; + color_position.y = col; + int begin = 0; int end = 0; bool valid = false; @@ -1612,10 +1614,15 @@ void ScriptTextEditor::_color_changed(const Color &p_color) { new_args = String("(" + rtos(p_color.r) + ", " + rtos(p_color.g) + ", " + rtos(p_color.b) + ", " + rtos(p_color.a) + ")"); } - String line = code_editor->get_text_edit()->get_line(color_line); - String new_line = line.replace(color_args, new_args); + String line = code_editor->get_text_edit()->get_line(color_position.x); + int color_args_pos = line.find(color_args, color_position.y); + String line_with_replaced_args = line; + line_with_replaced_args.erase(color_args_pos, color_args.length()); + line_with_replaced_args = line_with_replaced_args.insert(color_args_pos, new_args); + color_args = new_args; - code_editor->get_text_edit()->set_line(color_line, new_line); + code_editor->get_text_edit()->set_line(color_position.x, line_with_replaced_args); + code_editor->get_text_edit()->update(); } void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p_foldable, bool p_open_docs, bool p_goto_definition) { @@ -1710,6 +1717,7 @@ ScriptTextEditor::ScriptTextEditor() { color_panel = memnew(PopupPanel); add_child(color_panel); color_picker = memnew(ColorPicker); + color_picker->set_deferred_mode(true); color_panel->add_child(color_picker); color_picker->connect("color_changed", this, "_color_changed"); diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index 4dbade472c..4b2307555b 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -81,7 +81,7 @@ class ScriptTextEditor : public ScriptEditorBase { PopupPanel *color_panel; ColorPicker *color_picker; - int color_line; + Vector2 color_position; String color_args; void _update_member_keywords(); diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 88c5204074..863cf75a93 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -78,6 +78,7 @@ void VisualShaderEditor::edit(VisualShader *p_visual_shader) { hide(); } else { if (changed) { // to avoid tree collapse + _clear_buffer(); _update_options_menu(); } _update_graph(); @@ -210,6 +211,9 @@ void VisualShaderEditor::_update_options_menu() { if (!use_filter || add_options[i].name.findn(filter) != -1) { + if ((add_options[i].func != current_func && add_options[i].func != -1) || !_is_available(add_options[i].mode)) + continue; + if (prev_category != add_options[i].category) { if (category != NULL && item_count == 0) { memdelete(category); @@ -240,73 +244,53 @@ void VisualShaderEditor::_update_options_menu() { sub_category->set_collapsed(true); } } - if (sub_category != NULL) { - if ((add_options[i].func == current_func || add_options[i].func == -1) && _is_available(add_options[i].mode)) { - ++item_count2; - TreeItem *item = members->create_item(sub_category); - if (add_options[i].highend && low_driver) - item->set_custom_color(0, unsupported_color); - else if (add_options[i].highend) - item->set_custom_color(0, supported_color); - item->set_text(0, add_options[i].name); - if (is_first_item) { - item->select(0); - is_first_item = false; - } - switch (add_options[i].return_type) { - case VisualShaderNode::PORT_TYPE_SCALAR: - item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("float", "EditorIcons")); - break; - case VisualShaderNode::PORT_TYPE_VECTOR: - item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Vector3", "EditorIcons")); - break; - case VisualShaderNode::PORT_TYPE_BOOLEAN: - item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("bool", "EditorIcons")); - break; - case VisualShaderNode::PORT_TYPE_TRANSFORM: - item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Transform", "EditorIcons")); - break; - case VisualShaderNode::PORT_TYPE_COLOR: - item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Color", "EditorIcons")); - break; - default: - break; - } - item->set_meta("id", i); - } - } } else { - if (category != NULL) { - if ((add_options[i].func == current_func || add_options[i].func == -1) && _is_available(add_options[i].mode)) { - ++item_count; - TreeItem *item = members->create_item(category); - if (add_options[i].highend && low_driver) - item->set_custom_color(0, unsupported_color); - else if (add_options[i].highend) - item->set_custom_color(0, supported_color); - item->set_text(0, add_options[i].name); - switch (add_options[i].return_type) { - case VisualShaderNode::PORT_TYPE_SCALAR: - item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("float", "EditorIcons")); - break; - case VisualShaderNode::PORT_TYPE_VECTOR: - item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Vector3", "EditorIcons")); - break; - case VisualShaderNode::PORT_TYPE_BOOLEAN: - item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("bool", "EditorIcons")); - break; - case VisualShaderNode::PORT_TYPE_TRANSFORM: - item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Transform", "EditorIcons")); - break; - case VisualShaderNode::PORT_TYPE_COLOR: - item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Color", "EditorIcons")); - break; - default: - break; - } - item->set_meta("id", i); + sub_category = NULL; + } + + TreeItem *p_category = NULL; + + if (sub_category != NULL) { + p_category = sub_category; + ++item_count2; + } else if (category != NULL) { + p_category = category; + ++item_count; + } + + if (p_category != NULL) { + TreeItem *item = members->create_item(p_category); + if (add_options[i].highend && low_driver) + item->set_custom_color(0, unsupported_color); + else if (add_options[i].highend) + item->set_custom_color(0, supported_color); + item->set_text(0, add_options[i].name); + if (p_category == sub_category) { + if (is_first_item) { + item->select(0); + is_first_item = false; } } + switch (add_options[i].return_type) { + case VisualShaderNode::PORT_TYPE_SCALAR: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("float", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_VECTOR: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Vector3", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_BOOLEAN: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("bool", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_TRANSFORM: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Transform", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_COLOR: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Color", "EditorIcons")); + break; + default: + break; + } + item->set_meta("id", i); } prev_sub_category = add_options[i].sub_category; @@ -1594,22 +1578,38 @@ void VisualShaderEditor::_dup_copy_nodes(int p_type, List<int> &r_nodes, Set<int selection_center /= (float)r_nodes.size(); } -void VisualShaderEditor::_dup_paste_nodes(int p_type, List<int> &r_nodes, Set<int> &r_excluded, const Vector2 &p_offset, bool p_select) { +void VisualShaderEditor::_dup_paste_nodes(int p_type, int p_pasted_type, List<int> &r_nodes, Set<int> &r_excluded, const Vector2 &p_offset, bool p_select) { VisualShader::Type type = (VisualShader::Type)p_type; + VisualShader::Type pasted_type = (VisualShader::Type)p_pasted_type; int base_id = visual_shader->get_valid_node_id(type); int id_from = base_id; Map<int, int> connection_remap; + Set<int> unsupported_set; for (List<int>::Element *E = r_nodes.front(); E; E = E->next()) { connection_remap[E->get()] = id_from; - Ref<VisualShaderNode> node = visual_shader->get_node(type, E->get()); + Ref<VisualShaderNode> node = visual_shader->get_node(pasted_type, E->get()); + + bool unsupported = false; + for (int i = 0; i < add_options.size(); i++) { + if (add_options[i].type == node->get_class_name()) { + if (!_is_available(add_options[i].mode)) { + unsupported = true; + } + break; + } + } + if (unsupported) { + unsupported_set.insert(E->get()); + continue; + } Ref<VisualShaderNode> dupli = node->duplicate(); - undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, dupli, visual_shader->get_node_position(type, E->get()) + p_offset, id_from); + undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, dupli, visual_shader->get_node_position(pasted_type, E->get()) + p_offset, id_from); undo_redo->add_undo_method(visual_shader.ptr(), "remove_node", type, id_from); // duplicate size, inputs and outputs if node is group @@ -1629,9 +1629,12 @@ void VisualShaderEditor::_dup_paste_nodes(int p_type, List<int> &r_nodes, Set<in } List<VisualShader::Connection> conns; - visual_shader->get_node_connections(type, &conns); + visual_shader->get_node_connections(pasted_type, &conns); for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) { + if (unsupported_set.has(E->get().from_node) || unsupported_set.has(E->get().to_node)) { + continue; + } if (connection_remap.has(E->get().from_node) && connection_remap.has(E->get().to_node)) { undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes_forced", type, connection_remap[E->get().from_node], E->get().from_port, connection_remap[E->get().to_node], E->get().to_port); } @@ -1678,7 +1681,7 @@ void VisualShaderEditor::_duplicate_nodes() { undo_redo->create_action(TTR("Duplicate Nodes")); - _dup_paste_nodes(type, nodes, excluded, Vector2(10, 10) * EDSCALE, true); + _dup_paste_nodes(type, type, nodes, excluded, Vector2(10, 10) * EDSCALE, true); } void VisualShaderEditor::_copy_nodes() { @@ -1701,7 +1704,7 @@ void VisualShaderEditor::_paste_nodes() { float scale = graph->get_zoom(); - _dup_paste_nodes(type, copy_nodes_buffer, copy_nodes_excluded_buffer, (graph->get_scroll_ofs() / scale + graph->get_local_mouse_position() / scale - selection_center), false); + _dup_paste_nodes(type, copy_type, copy_nodes_buffer, copy_nodes_excluded_buffer, (graph->get_scroll_ofs() / scale + graph->get_local_mouse_position() / scale - selection_center), false); _dup_update_excluded(type, copy_nodes_excluded_buffer); // to prevent selection of previous copies at new paste } @@ -1780,9 +1783,6 @@ void VisualShaderEditor::_on_nodes_delete() { void VisualShaderEditor::_mode_selected(int p_id) { - copy_nodes_buffer.clear(); - copy_nodes_excluded_buffer.clear(); - _update_options_menu(); _update_graph(); } diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index 315ef1788e..d2b1e8bc45 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -178,7 +178,7 @@ class VisualShaderEditor : public VBoxContainer { void _dup_copy_nodes(int p_type, List<int> &r_nodes, Set<int> &r_excluded); void _dup_update_excluded(int p_type, Set<int> &r_excluded); - void _dup_paste_nodes(int p_type, List<int> &r_nodes, Set<int> &r_excluded, const Vector2 &p_offset, bool p_select); + void _dup_paste_nodes(int p_type, int p_pasted_type, List<int> &r_nodes, Set<int> &r_excluded, const Vector2 &p_offset, bool p_select); void _duplicate_nodes(); diff --git a/modules/tinyexr/image_saver_tinyexr.cpp b/modules/tinyexr/image_saver_tinyexr.cpp new file mode 100644 index 0000000000..e1d42d3217 --- /dev/null +++ b/modules/tinyexr/image_saver_tinyexr.cpp @@ -0,0 +1,279 @@ +/*************************************************************************/ +/* image_saver_tinyexr.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "image_saver_tinyexr.h" +#include "core/math/math_funcs.h" + +#include "thirdparty/tinyexr/tinyexr.h" + +static bool is_supported_format(Image::Format p_format) { + // This is checked before anything else. + // Mostly uncompressed formats are considered. + switch (p_format) { + case Image::FORMAT_RF: + case Image::FORMAT_RGF: + case Image::FORMAT_RGBF: + case Image::FORMAT_RGBAF: + case Image::FORMAT_RH: + case Image::FORMAT_RGH: + case Image::FORMAT_RGBH: + case Image::FORMAT_RGBAH: + case Image::FORMAT_R8: + case Image::FORMAT_RG8: + case Image::FORMAT_RGB8: + case Image::FORMAT_RGBA8: + return true; + default: + return false; + } +} + +enum SrcPixelType { + SRC_FLOAT, + SRC_HALF, + SRC_BYTE +}; + +static SrcPixelType get_source_pixel_type(Image::Format p_format) { + switch (p_format) { + case Image::FORMAT_RF: + case Image::FORMAT_RGF: + case Image::FORMAT_RGBF: + case Image::FORMAT_RGBAF: + return SRC_FLOAT; + case Image::FORMAT_RH: + case Image::FORMAT_RGH: + case Image::FORMAT_RGBH: + case Image::FORMAT_RGBAH: + return SRC_HALF; + case Image::FORMAT_R8: + case Image::FORMAT_RG8: + case Image::FORMAT_RGB8: + case Image::FORMAT_RGBA8: + return SRC_BYTE; + default: + CRASH_NOW(); + } +} + +static int get_target_pixel_type(Image::Format p_format) { + switch (p_format) { + case Image::FORMAT_RF: + case Image::FORMAT_RGF: + case Image::FORMAT_RGBF: + case Image::FORMAT_RGBAF: + return TINYEXR_PIXELTYPE_FLOAT; + case Image::FORMAT_RH: + case Image::FORMAT_RGH: + case Image::FORMAT_RGBH: + case Image::FORMAT_RGBAH: + // EXR doesn't support 8-bit channels so in that case we'll convert + case Image::FORMAT_R8: + case Image::FORMAT_RG8: + case Image::FORMAT_RGB8: + case Image::FORMAT_RGBA8: + return TINYEXR_PIXELTYPE_HALF; + default: + CRASH_NOW(); + } +} + +static int get_pixel_type_size(int p_pixel_type) { + switch (p_pixel_type) { + case TINYEXR_PIXELTYPE_HALF: + return 2; + case TINYEXR_PIXELTYPE_FLOAT: + return 4; + } + CRASH_NOW(); +} + +static int get_channel_count(Image::Format p_format) { + switch (p_format) { + case Image::FORMAT_RF: + case Image::FORMAT_RH: + case Image::FORMAT_R8: + return 1; + case Image::FORMAT_RGF: + case Image::FORMAT_RGH: + case Image::FORMAT_RG8: + return 2; + case Image::FORMAT_RGBF: + case Image::FORMAT_RGBH: + case Image::FORMAT_RGB8: + return 3; + case Image::FORMAT_RGBAF: + case Image::FORMAT_RGBAH: + case Image::FORMAT_RGBA8: + return 4; + default: + CRASH_NOW(); + } +} + +Error save_exr(const String &p_path, const Ref<Image> &p_img, bool p_grayscale) { + + Image::Format format = p_img->get_format(); + + if (!is_supported_format(format)) { + // Format not supported + print_error("Image format not supported for saving as EXR. Consider saving as PNG."); + return ERR_UNAVAILABLE; + } + + EXRHeader header; + InitEXRHeader(&header); + + EXRImage image; + InitEXRImage(&image); + + const int max_channels = 4; + + // Godot does not support more than 4 channels, + // so we can preallocate header infos on the stack and use only the subset we need + PoolByteArray channels[max_channels]; + unsigned char *channels_ptrs[max_channels]; + EXRChannelInfo channel_infos[max_channels]; + int pixel_types[max_channels]; + int requested_pixel_types[max_channels] = { -1 }; + + // Gimp and Blender are a bit annoying so order of channels isn't straightforward. + const int channel_mappings[4][4] = { + { 0 }, // R + { 1, 0 }, // GR + { 2, 1, 0 }, // BGR + { 2, 1, 0, 3 } // BGRA + }; + + int channel_count = get_channel_count(format); + ERR_FAIL_COND_V(p_grayscale && channel_count != 1, ERR_INVALID_PARAMETER); + + int target_pixel_type = get_target_pixel_type(format); + int target_pixel_type_size = get_pixel_type_size(target_pixel_type); + SrcPixelType src_pixel_type = get_source_pixel_type(format); + const int pixel_count = p_img->get_width() * p_img->get_height(); + + const int *channel_mapping = channel_mappings[channel_count - 1]; + + { + PoolByteArray src_data = p_img->get_data(); + PoolByteArray::Read src_r = src_data.read(); + + for (int channel_index = 0; channel_index < channel_count; ++channel_index) { + + // De-interleave channels + + PoolByteArray &dst = channels[channel_index]; + dst.resize(pixel_count * target_pixel_type_size); + + PoolByteArray::Write dst_w = dst.write(); + + if (src_pixel_type == SRC_FLOAT && target_pixel_type == TINYEXR_PIXELTYPE_FLOAT) { + + // Note: we don't save mipmaps + CRASH_COND(src_data.size() < pixel_count * channel_count * target_pixel_type_size); + + const float *src_rp = (float *)src_r.ptr(); + float *dst_wp = (float *)dst_w.ptr(); + + for (int i = 0; i < pixel_count; ++i) { + dst_wp[i] = src_rp[channel_index + i * channel_count]; + } + + } else if (src_pixel_type == SRC_HALF && target_pixel_type == TINYEXR_PIXELTYPE_HALF) { + + CRASH_COND(src_data.size() < pixel_count * channel_count * target_pixel_type_size); + + const uint16_t *src_rp = (uint16_t *)src_r.ptr(); + uint16_t *dst_wp = (uint16_t *)dst_w.ptr(); + + for (int i = 0; i < pixel_count; ++i) { + dst_wp[i] = src_rp[channel_index + i * channel_count]; + } + + } else if (src_pixel_type == SRC_BYTE && target_pixel_type == TINYEXR_PIXELTYPE_HALF) { + + CRASH_COND(src_data.size() < pixel_count * channel_count); + + const uint8_t *src_rp = (uint8_t *)src_r.ptr(); + uint16_t *dst_wp = (uint16_t *)dst_w.ptr(); + + for (int i = 0; i < pixel_count; ++i) { + dst_wp[i] = Math::make_half_float(src_rp[channel_index + i * channel_count] / 255.f); + } + + } else { + CRASH_NOW(); + } + + int remapped_index = channel_mapping[channel_index]; + + channels_ptrs[remapped_index] = dst_w.ptr(); + + // No conversion + pixel_types[remapped_index] = target_pixel_type; + requested_pixel_types[remapped_index] = target_pixel_type; + + // Write channel name + if (p_grayscale) { + channel_infos[remapped_index].name[0] = 'Y'; + channel_infos[remapped_index].name[1] = '\0'; + } else { + const char *rgba = "RGBA"; + channel_infos[remapped_index].name[0] = rgba[channel_index]; + channel_infos[remapped_index].name[1] = '\0'; + } + } + } + + image.images = channels_ptrs; + image.num_channels = channel_count; + image.width = p_img->get_width(); + image.height = p_img->get_height(); + + header.num_channels = image.num_channels; + header.channels = channel_infos; + header.pixel_types = pixel_types; + header.requested_pixel_types = requested_pixel_types; + // TODO DEBUG REMOVE + for (int i = 0; i < 4; ++i) { + print_line(String("requested_pixel_types{0}: {1}").format(varray(i, requested_pixel_types[i]))); + } + + CharString utf8_filename = p_path.utf8(); + const char *err; + int ret = SaveEXRImageToFile(&image, &header, utf8_filename.ptr(), &err); + if (ret != TINYEXR_SUCCESS) { + print_error(String("Saving EXR failed. Error: {0}").format(varray(err))); + return ERR_FILE_CANT_WRITE; + } + + return OK; +} diff --git a/modules/tinyexr/image_saver_tinyexr.h b/modules/tinyexr/image_saver_tinyexr.h new file mode 100644 index 0000000000..298bd1d21c --- /dev/null +++ b/modules/tinyexr/image_saver_tinyexr.h @@ -0,0 +1,38 @@ +/*************************************************************************/ +/* image_saver_tinyexr.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 IMAGE_SAVER_TINYEXR_H +#define IMAGE_SAVER_TINYEXR_H + +#include "core/os/os.h" + +Error save_exr(const String &p_path, const Ref<Image> &p_img, bool p_grayscale); + +#endif // IMAGE_SAVER_TINYEXR_H diff --git a/modules/tinyexr/register_types.cpp b/modules/tinyexr/register_types.cpp index 5473a55687..233b3afa08 100644 --- a/modules/tinyexr/register_types.cpp +++ b/modules/tinyexr/register_types.cpp @@ -31,6 +31,7 @@ #include "register_types.h" #include "image_loader_tinyexr.h" +#include "image_saver_tinyexr.h" static ImageLoaderTinyEXR *image_loader_tinyexr = NULL; @@ -38,9 +39,13 @@ void register_tinyexr_types() { image_loader_tinyexr = memnew(ImageLoaderTinyEXR); ImageLoader::add_image_format_loader(image_loader_tinyexr); + + Image::save_exr_func = save_exr; } void unregister_tinyexr_types() { memdelete(image_loader_tinyexr); + + Image::save_exr_func = NULL; } diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 1b9d31d752..2fba8bbf7f 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -847,6 +847,138 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { uint32_t name = decode_uint32(&p_manifest[iofs + 12]); String tname = string_table[name]; + int dof_index = p_preset->get("graphics/degrees_of_freedom"); // 0: none, 1: 3dof and 6dof, 2: 6dof + + if (tname == "uses-feature" && dof_index > 0) { + if (xr_mode_index == 0) { + WARN_PRINT("VR DOF feature setting is only valid for oculus HMDs with an XR mode set to VR"); + } + ofs += 24; // skip over end tag + + // save manifest ending so we can restore it + Vector<uint8_t> manifest_end; + uint32_t manifest_cur_size = p_manifest.size(); + + manifest_end.resize(p_manifest.size() - ofs); + memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size()); + + int32_t attr_name_string = string_table.find("name"); + ERR_EXPLAIN("Template does not have 'name' attribute"); + ERR_FAIL_COND(attr_name_string == -1); + + int32_t ns_android_string = string_table.find("http://schemas.android.com/apk/res/android"); + if (ns_android_string == -1) { + string_table.push_back("http://schemas.android.com/apk/res/android"); + ns_android_string = string_table.size() - 1; + } + + int32_t attr_uses_permission_string = string_table.find("uses-feature"); + if (attr_uses_permission_string == -1) { + string_table.push_back("uses-feature"); + attr_uses_permission_string = string_table.size() - 1; + } + + int32_t attr_required_string = string_table.find("required"); + if (attr_required_string == -1) { + string_table.push_back("required"); + attr_required_string = string_table.size() - 1; + } + + int32_t attr_version_string = string_table.find("version"); + if (attr_version_string == -1) { + string_table.push_back("version"); + attr_version_string = string_table.size() - 1; + } + + String required_value_string; + if (dof_index == 1) { + required_value_string = "false"; + } else if (dof_index == 2) { + required_value_string = "true"; + } else { + ERR_EXPLAIN("Unknown dof index " + itos(dof_index)); + ERR_FAIL(); + } + int32_t required_value = string_table.find(required_value_string); + if (required_value == -1) { + string_table.push_back(required_value_string); + required_value = string_table.size() - 1; + } + + int32_t version_value = string_table.find("1"); + if (version_value == -1) { + string_table.push_back("1"); + version_value = string_table.size() - 1; + } + + int32_t feature_string = string_table.find("android.hardware.vr.headtracking"); + if (feature_string == -1) { + string_table.push_back("android.hardware.vr.headtracking"); + feature_string = string_table.size() - 1; + } + + { + manifest_cur_size += 96 + 20; // node and three attrs + end node + p_manifest.resize(manifest_cur_size); + + // start tag + encode_uint16(0x102, &p_manifest.write[ofs]); // type + encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize + encode_uint32(96, &p_manifest.write[ofs + 4]); // size + encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno + encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment + encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns + encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name + encode_uint16(20, &p_manifest.write[ofs + 24]); // attr_start + encode_uint16(20, &p_manifest.write[ofs + 26]); // attr_size + encode_uint16(3, &p_manifest.write[ofs + 28]); // num_attrs + encode_uint16(0, &p_manifest.write[ofs + 30]); // id_index + encode_uint16(0, &p_manifest.write[ofs + 32]); // class_index + encode_uint16(0, &p_manifest.write[ofs + 34]); // style_index + + // android:name attribute + encode_uint32(ns_android_string, &p_manifest.write[ofs + 36]); // ns + encode_uint32(attr_name_string, &p_manifest.write[ofs + 40]); // 'name' + encode_uint32(feature_string, &p_manifest.write[ofs + 44]); // raw_value + encode_uint16(8, &p_manifest.write[ofs + 48]); // typedvalue_size + p_manifest.write[ofs + 50] = 0; // typedvalue_always0 + p_manifest.write[ofs + 51] = 0x03; // typedvalue_type (string) + encode_uint32(feature_string, &p_manifest.write[ofs + 52]); // typedvalue reference + + // android:required attribute + encode_uint32(ns_android_string, &p_manifest.write[ofs + 56]); // ns + encode_uint32(attr_required_string, &p_manifest.write[ofs + 60]); // 'name' + encode_uint32(required_value, &p_manifest.write[ofs + 64]); // raw_value + encode_uint16(8, &p_manifest.write[ofs + 68]); // typedvalue_size + p_manifest.write[ofs + 70] = 0; // typedvalue_always0 + p_manifest.write[ofs + 71] = 0x03; // typedvalue_type (string) + encode_uint32(required_value, &p_manifest.write[ofs + 72]); // typedvalue reference + + // android:version attribute + encode_uint32(ns_android_string, &p_manifest.write[ofs + 76]); // ns + encode_uint32(attr_version_string, &p_manifest.write[ofs + 80]); // 'name' + encode_uint32(version_value, &p_manifest.write[ofs + 84]); // raw_value + encode_uint16(8, &p_manifest.write[ofs + 88]); // typedvalue_size + p_manifest.write[ofs + 90] = 0; // typedvalue_always0 + p_manifest.write[ofs + 91] = 0x03; // typedvalue_type (string) + encode_uint32(version_value, &p_manifest.write[ofs + 92]); // typedvalue reference + + ofs += 96; + + // end tag + encode_uint16(0x103, &p_manifest.write[ofs]); // type + encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize + encode_uint32(24, &p_manifest.write[ofs + 4]); // size + encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno + encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment + encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns + encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name + + ofs += 24; + } + memcpy(&p_manifest.write[ofs], manifest_end.ptr(), manifest_end.size()); + ofs -= 24; // go back over back end + } if (tname == "manifest") { // save manifest ending so we can restore it @@ -1156,6 +1288,7 @@ public: virtual void get_export_options(List<ExportOption> *r_options) { r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "graphics/xr_mode", PROPERTY_HINT_ENUM, "Regular,Oculus Mobile VR"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "graphics/degrees_of_freedom", PROPERTY_HINT_ENUM, "None,3DOF and 6DOF,6DOF"), 0)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/32_bits_framebuffer"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), "")); diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index c79cd80e2e..2cd05b5c50 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -1203,6 +1203,8 @@ void TileMap::clear() { void TileMap::_set_tile_data(const PoolVector<int> &p_data) { + ERR_FAIL_COND(format > FORMAT_2); + int c = p_data.size(); PoolVector<int>::Read r = p_data.read(); @@ -1245,8 +1247,6 @@ void TileMap::_set_tile_data(const PoolVector<int> &p_data) { set_cell(x, y, v, flip_h, flip_v, transpose, Vector2(coord_x, coord_y)); } - - format = FORMAT_2; } PoolVector<int> TileMap::_get_tile_data() const { @@ -1255,7 +1255,7 @@ PoolVector<int> TileMap::_get_tile_data() const { data.resize(tile_map.size() * 3); PoolVector<int>::Write w = data.write(); - format = FORMAT_2; + // Save in highest format int idx = 0; for (const Map<PosKey, Cell>::Element *E = tile_map.front(); E; E = E->next()) { @@ -1560,7 +1560,7 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) { if (p_name == "format") { if (p_value.get_type() == Variant::INT) { - format = (DataFormat)(p_value.operator int64_t()); + format = (DataFormat)(p_value.operator int64_t()); // Set format used for loading return true; } } else if (p_name == "tile_data") { @@ -1576,7 +1576,7 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) { bool TileMap::_get(const StringName &p_name, Variant &r_ret) const { if (p_name == "format") { - r_ret = format; + r_ret = FORMAT_2; // When saving, always save highest format return true; } else if (p_name == "tile_data") { r_ret = _get_tile_data(); @@ -1909,6 +1909,8 @@ void TileMap::_bind_methods() { ADD_GROUP("Occluder", "occluder_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "occluder_light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_occluder_light_mask", "get_occluder_light_mask"); + ADD_PROPERTY_DEFAULT("format", FORMAT_1); + ADD_SIGNAL(MethodInfo("settings_changed")); BIND_CONSTANT(INVALID_CELL); @@ -1957,7 +1959,7 @@ TileMap::TileMap() { centered_textures = false; occluder_light_mask = 1; clip_uv = false; - format = FORMAT_1; //Always initialize with the lowest format + format = FORMAT_1; // Assume lowest possible format if none is present fp_adjust = 0.00001; tile_origin = TILE_ORIGIN_TOP_LEFT; diff --git a/scene/main/node.cpp b/scene/main/node.cpp index f96e7a043d..beb7356bc7 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1265,6 +1265,7 @@ void Node::remove_child(Node *p_child) { } } + ERR_EXPLAIN("Cannot remove child node " + p_child->to_string() + " as it is not in our list of children"); ERR_FAIL_COND(idx == -1); //ERR_FAIL_COND( p_child->data.blocked > 0 ); @@ -1664,6 +1665,7 @@ NodePath Node::get_path_to(const Node *p_node) const { NodePath Node::get_path() const { + ERR_EXPLAIN("Cannot get path of node as it is not in a scene tree"); ERR_FAIL_COND_V(!is_inside_tree(), NodePath()); if (data.path_cache) diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index 7c3867beaa..0f0974114f 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -1047,8 +1047,8 @@ void Environment::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fog_transmit_enabled"), "set_fog_transmit_enabled", "is_fog_transmit_enabled"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "fog_transmit_curve", PROPERTY_HINT_EXP_EASING), "set_fog_transmit_curve", "get_fog_transmit_curve"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fog_height_enabled"), "set_fog_height_enabled", "is_fog_height_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "fog_height_min", PROPERTY_HINT_RANGE, "-4000,4000,0.1"), "set_fog_height_min", "get_fog_height_min"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "fog_height_max", PROPERTY_HINT_RANGE, "-4000,4000,0.1"), "set_fog_height_max", "get_fog_height_max"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "fog_height_min", PROPERTY_HINT_RANGE, "-4000,4000,0.1,or_lesser,or_greater"), "set_fog_height_min", "get_fog_height_min"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "fog_height_max", PROPERTY_HINT_RANGE, "-4000,4000,0.1,or_lesser,or_greater"), "set_fog_height_max", "get_fog_height_max"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "fog_height_curve", PROPERTY_HINT_EXP_EASING), "set_fog_height_curve", "get_fog_height_curve"); ClassDB::bind_method(D_METHOD("set_tonemapper", "mode"), &Environment::set_tonemapper); @@ -1406,8 +1406,8 @@ Environment::Environment() : fog_transmit_curve = 1; fog_height_enabled = false; - fog_height_min = 0; - fog_height_max = 100; + fog_height_min = 10; + fog_height_max = 0; fog_height_curve = 1; set_fog_color(Color(0.5, 0.6, 0.7)); |