diff options
138 files changed, 2114 insertions, 990 deletions
diff --git a/SConstruct b/SConstruct index 4ef302cac4..7a8db10931 100644 --- a/SConstruct +++ b/SConstruct @@ -65,7 +65,7 @@ platform_arg = ARGUMENTS.get("platform", ARGUMENTS.get("p", False)) if (os.name == "posix"): pass elif (os.name == "nt"): - if (os.getenv("VCINSTALLDIR") == None or platform_arg == "android" or platform_arg == "javascript"): + if platform_arg == "android" or platform_arg == "javascript" or ARGUMENTS.get("use_mingw", False): custom_tools = ['mingw'] env_base = Environment(tools=custom_tools) @@ -92,6 +92,7 @@ env_base.use_ptrcall = False env_base.split_drivers = False env_base.split_modules = False env_base.module_version_string = "" +env_base.msvc = False # To decide whether to rebuild a file, use the MD5 sum only if the timestamp has changed. # http://scons.org/doc/production/HTML/scons-user/ch06.html#idm139837621851792 @@ -240,14 +241,13 @@ sys.modules.pop('detect') """ if (env_base['target'] == 'debug'): - env_base.Append(CPPFLAGS=['-DDEBUG_MEMORY_ALLOC']) - env_base.Append(CPPFLAGS=['-DSCI_NAMESPACE']) + env_base.Append(CPPDEFINES=['DEBUG_MEMORY_ALLOC', 'SCI_NAMESPACE']) if (env_base['no_editor_splash']): - env_base.Append(CPPFLAGS=['-DNO_EDITOR_SPLASH']) + env_base.Append(CPPDEFINES=['NO_EDITOR_SPLASH']) if not env_base['deprecated']: - env_base.Append(CPPFLAGS=['-DDISABLE_DEPRECATED']) + env_base.Append(CPPDEFINES=['DISABLE_DEPRECATED']) env_base.platforms = {} @@ -329,9 +329,7 @@ if selected_platform in platform_list: if (env["warnings"] == 'yes'): print("WARNING: warnings=yes is deprecated; assuming warnings=all") - env.msvc = 0 - if (os.name == "nt" and os.getenv("VCINSTALLDIR") and (platform_arg == "windows" or platform_arg == "uwp")): # MSVC, needs to stand out of course - env.msvc = 1 + if env.msvc: disable_nonessential_warnings = ['/wd4267', '/wd4244', '/wd4305', '/wd4800'] # Truncations, narrowing conversions... if (env["warnings"] == 'extra'): env.Append(CCFLAGS=['/Wall']) # Implies /W4 @@ -363,7 +361,7 @@ if selected_platform in platform_list: print("Tools can only be built with targets 'debug' and 'release_debug'.") sys.exit(255) suffix += ".opt" - env.Append(CCFLAGS=['-DNDEBUG']) + env.Append(CPPDEFINES=['NDEBUG']) elif (env["target"] == "release_debug"): if env["tools"]: @@ -420,25 +418,25 @@ if selected_platform in platform_list: env["SHLIBSUFFIX"] = suffix + env["SHLIBSUFFIX"] if (env.use_ptrcall): - env.Append(CPPFLAGS=['-DPTRCALL_ENABLED']) + env.Append(CPPDEFINES=['PTRCALL_ENABLED']) # to test 64 bits compiltion # env.Append(CPPFLAGS=['-m64']) if env['tools']: - env.Append(CPPFLAGS=['-DTOOLS_ENABLED']) + env.Append(CPPDEFINES=['TOOLS_ENABLED']) if env['disable_3d']: - env.Append(CPPFLAGS=['-D_3D_DISABLED']) + env.Append(CPPDEFINES=['_3D_DISABLED']) if env['gdscript']: - env.Append(CPPFLAGS=['-DGDSCRIPT_ENABLED']) + env.Append(CPPDEFINES=['GDSCRIPT_ENABLED']) if env['disable_advanced_gui']: - env.Append(CPPFLAGS=['-DADVANCED_GUI_DISABLED']) + env.Append(CPPDEFINES=['ADVANCED_GUI_DISABLED']) if env['minizip']: - env.Append(CPPFLAGS=['-DMINIZIP_ENABLED']) + env.Append(CPPDEFINES=['MINIZIP_ENABLED']) if env['xml']: - env.Append(CPPFLAGS=['-DXML_ENABLED']) + env.Append(CPPDEFINES=['XML_ENABLED']) if not env['verbose']: methods.no_verbose(sys, env) @@ -479,10 +477,7 @@ if selected_platform in platform_list: if ("check_c_headers" in env): for header in env["check_c_headers"]: if (conf.CheckCHeader(header[0])): - if (env.msvc): - env.Append(CCFLAGS=['/D' + header[1]]) - else: - env.Append(CCFLAGS=['-D' + header[1]]) + env.AppendUnique(CPPDEFINES=[header[1]]) else: @@ -493,119 +488,118 @@ else: print("\nPlease run scons again with argument: platform=<string>") -screen = sys.stdout -node_count = 0 -node_count_max = 0 -node_count_interval = 1 -if ('env' in locals()): +# The following only makes sense when the env is defined, and assumes it is +if 'env' in locals(): + screen = sys.stdout + # Progress reporting is not available in non-TTY environments since it + # messes with the output (for example, when writing to a file) + show_progress = (env['progress'] and sys.stdout.isatty()) + node_count = 0 + node_count_max = 0 + node_count_interval = 1 node_count_fname = str(env.Dir('#')) + '/.scons_node_count' -# Progress reporting is not available in non-TTY environments since it -# messes with the output (for example, when writing to a file) -if sys.stdout.isatty(): - show_progress = env['progress'] -else: - show_progress = False - -import time, math - -class cache_progress: - # The default is 1 GB cache and 12 hours half life - def __init__(self, path = None, limit = 1073741824, half_life = 43200): - self.path = path - self.limit = limit - self.exponent_scale = math.log(2) / half_life - if env['verbose'] and path != None: - screen.write('Current cache limit is ' + self.convert_size(limit) + ' (used: ' + self.convert_size(self.get_size(path)) + ')\n') - self.delete(self.file_list()) - - def __call__(self, node, *args, **kw): - global node_count, node_count_max, node_count_interval, node_count_fname, show_progress - if show_progress: - # Print the progress percentage - node_count += node_count_interval - if (node_count_max > 0 and node_count <= node_count_max): - screen.write('\r[%3d%%] ' % (node_count * 100 / node_count_max)) - screen.flush() - elif (node_count_max > 0 and node_count > node_count_max): - screen.write('\r[100%] ') - screen.flush() + + import time, math + + class cache_progress: + # The default is 1 GB cache and 12 hours half life + def __init__(self, path = None, limit = 1073741824, half_life = 43200): + self.path = path + self.limit = limit + self.exponent_scale = math.log(2) / half_life + if env['verbose'] and path != None: + screen.write('Current cache limit is ' + self.convert_size(limit) + ' (used: ' + self.convert_size(self.get_size(path)) + ')\n') + self.delete(self.file_list()) + + def __call__(self, node, *args, **kw): + global node_count, node_count_max, node_count_interval, node_count_fname, show_progress + if show_progress: + # Print the progress percentage + node_count += node_count_interval + if (node_count_max > 0 and node_count <= node_count_max): + screen.write('\r[%3d%%] ' % (node_count * 100 / node_count_max)) + screen.flush() + elif (node_count_max > 0 and node_count > node_count_max): + screen.write('\r[100%] ') + screen.flush() + else: + screen.write('\r[Initial build] ') + screen.flush() + + def delete(self, files): + if len(files) == 0: + return + if env['verbose']: + # Utter something + screen.write('\rPurging %d %s from cache...\n' % (len(files), len(files) > 1 and 'files' or 'file')) + [os.remove(f) for f in files] + + def file_list(self): + if self.path == None: + # Nothing to do + return [] + # Gather a list of (filename, (size, atime)) within the + # cache directory + file_stat = [(x, os.stat(x)[6:8]) for x in glob.glob(os.path.join(self.path, '*', '*'))] + if file_stat == []: + # Nothing to do + return [] + # Weight the cache files by size (assumed to be roughly + # proportional to the recompilation time) times an exponential + # decay since the ctime, and return a list with the entries + # (filename, size, weight). + current_time = time.time() + file_stat = [(x[0], x[1][0], (current_time - x[1][1])) for x in file_stat] + # Sort by the most resently accessed files (most sensible to keep) first + file_stat.sort(key=lambda x: x[2]) + # Search for the first entry where the storage limit is + # reached + sum, mark = 0, None + for i,x in enumerate(file_stat): + sum += x[1] + if sum > self.limit: + mark = i + break + if mark == None: + return [] else: - screen.write('\r[Initial build] ') - screen.flush() - - def delete(self, files): - if len(files) == 0: - return - if env['verbose']: - # Utter something - screen.write('\rPurging %d %s from cache...\n' % (len(files), len(files) > 1 and 'files' or 'file')) - [os.remove(f) for f in files] - - def file_list(self): - if self.path == None: - # Nothing to do - return [] - # Gather a list of (filename, (size, atime)) within the - # cache directory - file_stat = [(x, os.stat(x)[6:8]) for x in glob.glob(os.path.join(self.path, '*', '*'))] - if file_stat == []: - # Nothing to do - return [] - # Weight the cache files by size (assumed to be roughly - # proportional to the recompilation time) times an exponential - # decay since the ctime, and return a list with the entries - # (filename, size, weight). - current_time = time.time() - file_stat = [(x[0], x[1][0], (current_time - x[1][1])) for x in file_stat] - # Sort by the most resently accessed files (most sensible to keep) first - file_stat.sort(key=lambda x: x[2]) - # Search for the first entry where the storage limit is - # reached - sum, mark = 0, None - for i,x in enumerate(file_stat): - sum += x[1] - if sum > self.limit: - mark = i - break - if mark == None: - return [] - else: - return [x[0] for x in file_stat[mark:]] - - def convert_size(self, size_bytes): - if size_bytes == 0: - return "0 bytes" - size_name = ("bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB") - i = int(math.floor(math.log(size_bytes, 1024))) - p = math.pow(1024, i) - s = round(size_bytes / p, 2) - return "%s %s" % (int(s) if i == 0 else s, size_name[i]) - - def get_size(self, start_path = '.'): - total_size = 0 - for dirpath, dirnames, filenames in os.walk(start_path): - for f in filenames: - fp = os.path.join(dirpath, f) - total_size += os.path.getsize(fp) - return total_size - -def progress_finish(target, source, env): - global node_count, progressor - with open(node_count_fname, 'w') as f: - f.write('%d\n' % node_count) - progressor.delete(progressor.file_list()) - -try: - with open(node_count_fname) as f: - node_count_max = int(f.readline()) -except: - pass -cache_directory = os.environ.get("SCONS_CACHE") -# Simple cache pruning, attached to SCons' progress callback. Trim the -# cache directory to a size not larger than cache_limit. -cache_limit = float(os.getenv("SCONS_CACHE_LIMIT", 1024)) * 1024 * 1024 -progressor = cache_progress(cache_directory, cache_limit) -Progress(progressor, interval = node_count_interval) - -progress_finish_command = Command('progress_finish', [], progress_finish) -AlwaysBuild(progress_finish_command) + return [x[0] for x in file_stat[mark:]] + + def convert_size(self, size_bytes): + if size_bytes == 0: + return "0 bytes" + size_name = ("bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB") + i = int(math.floor(math.log(size_bytes, 1024))) + p = math.pow(1024, i) + s = round(size_bytes / p, 2) + return "%s %s" % (int(s) if i == 0 else s, size_name[i]) + + def get_size(self, start_path = '.'): + total_size = 0 + for dirpath, dirnames, filenames in os.walk(start_path): + for f in filenames: + fp = os.path.join(dirpath, f) + total_size += os.path.getsize(fp) + return total_size + + def progress_finish(target, source, env): + global node_count, progressor + with open(node_count_fname, 'w') as f: + f.write('%d\n' % node_count) + progressor.delete(progressor.file_list()) + + try: + with open(node_count_fname) as f: + node_count_max = int(f.readline()) + except: + pass + + cache_directory = os.environ.get("SCONS_CACHE") + # Simple cache pruning, attached to SCons' progress callback. Trim the + # cache directory to a size not larger than cache_limit. + cache_limit = float(os.getenv("SCONS_CACHE_LIMIT", 1024)) * 1024 * 1024 + progressor = cache_progress(cache_directory, cache_limit) + Progress(progressor, interval = node_count_interval) + + progress_finish_command = Command('progress_finish', [], progress_finish) + AlwaysBuild(progress_finish_command) diff --git a/core/SCsub b/core/SCsub index af83b49fea..383aaf0e12 100644 --- a/core/SCsub +++ b/core/SCsub @@ -18,9 +18,8 @@ gd_cpp = '#include "project_settings.h"\n' gd_cpp += gd_inc gd_cpp += "void ProjectSettings::register_global_defaults() {\n" + gd_call + "\n}\n" -f = open("global_defaults.gen.cpp", "w") -f.write(gd_cpp) -f.close() +with open("global_defaults.gen.cpp", "w") as f: + f.write(gd_cpp) # Generate AES256 script encryption key @@ -47,9 +46,8 @@ if ("SCRIPT_AES256_ENCRYPTION_KEY" in os.environ): txt = "0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0" print("Invalid AES256 encryption key, not 64 bits hex: " + e) -f = open("script_encryption_key.gen.cpp", "w") -f.write("#include \"project_settings.h\"\nuint8_t script_encryption_key[32]={" + txt + "};\n") -f.close() +with open("script_encryption_key.gen.cpp", "w") as f: + f.write("#include \"project_settings.h\"\nuint8_t script_encryption_key[32]={" + txt + "};\n") # Add required thirdparty code. Header paths are hardcoded, we don't need to append @@ -68,7 +66,6 @@ thirdparty_sources = [ "md5.cpp", "pcg.cpp", "triangulator.cpp", - "clipper.cpp", ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] env.add_source_files(env.core_sources, thirdparty_sources) diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index a81301e8ea..20adf7ff02 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -1535,6 +1535,17 @@ bool _File::is_open() const { return f != NULL; } +String _File::get_path() const { + + ERR_FAIL_COND_V(!f, ""); + return f->get_path(); +} + +String _File::get_path_absolute() const { + + ERR_FAIL_COND_V(!f, ""); + return f->get_path_absolute(); +} void _File::seek(int64_t p_position) { @@ -1824,6 +1835,8 @@ void _File::_bind_methods() { ClassDB::bind_method(D_METHOD("open", "path", "flags"), &_File::open); ClassDB::bind_method(D_METHOD("close"), &_File::close); + ClassDB::bind_method(D_METHOD("get_path"), &_File::get_path); + ClassDB::bind_method(D_METHOD("get_path_absolute"), &_File::get_path_absolute); ClassDB::bind_method(D_METHOD("is_open"), &_File::is_open); ClassDB::bind_method(D_METHOD("seek", "position"), &_File::seek); ClassDB::bind_method(D_METHOD("seek_end", "position"), &_File::seek_end, DEFVAL(0)); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 8ce7b4f7fe..c40abd51f6 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -415,6 +415,9 @@ public: void close(); ///< close a file bool is_open() const; ///< true when file is open + String get_path() const; /// returns the path for the current open file + String get_path_absolute() const; /// returns the absolute path for the current open file + void seek(int64_t p_position); ///< seek to a given position void seek_end(int64_t p_position = 0); ///< seek from the end of file int64_t get_position() const; ///< get position in the file diff --git a/core/color.cpp b/core/color.cpp index 27d8e9b891..b2f5889166 100644 --- a/core/color.cpp +++ b/core/color.cpp @@ -402,6 +402,10 @@ String Color::to_html(bool p_alpha) const { Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) { + p_h = Math::fmod(p_h * 360.0f, 360.0f); + if (p_h < 0.0) + p_h += 360.0f; + const float h_ = p_h / 60.0f; const float c = p_v * p_s; const float x = c * (1.0f - Math::abs(Math::fmod(h_, 2.0f) - 1.0f)); diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 9e21287780..2ebe8d6df7 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -813,7 +813,7 @@ static void _encode_string(const String &p_string, uint8_t *&buf, int &r_len) { while (r_len % 4) { r_len++; //pad if (buf) { - buf++; + *(buf++) = 0; } } } diff --git a/core/make_binders.py b/core/make_binders.py index 1e581f8ce3..6a7602cd5d 100644 --- a/core/make_binders.py +++ b/core/make_binders.py @@ -265,10 +265,8 @@ def run(target, source, env): else: text += t - f = open(target[0].path, "w") - f.write(text) - f.close() + with open(target[0].path, "w") as f: + f.write(text) - f = open(target[1].path, "w") - f.write(text_ext) - f.close() + with open(target[1].path, "w") as f: + f.write(text_ext) diff --git a/core/math/math_2d.cpp b/core/math/math_2d.cpp index d2e4101999..3767d298a1 100644 --- a/core/math/math_2d.cpp +++ b/core/math/math_2d.cpp @@ -98,11 +98,6 @@ real_t Vector2::cross(const Vector2 &p_other) const { return x * p_other.y - y * p_other.x; } -Vector2 Vector2::cross(real_t p_other) const { - - return Vector2(p_other * y, -p_other * x); -} - Vector2 Vector2::floor() const { return Vector2(Math::floor(x), Math::floor(y)); diff --git a/core/math/math_2d.h b/core/math/math_2d.h index 8928349a44..02d921b67e 100644 --- a/core/math/math_2d.h +++ b/core/math/math_2d.h @@ -104,7 +104,6 @@ struct Vector2 { real_t dot(const Vector2 &p_other) const; real_t cross(const Vector2 &p_other) const; - Vector2 cross(real_t p_other) const; Vector2 project(const Vector2 &p_vec) const; Vector2 plane_project(real_t p_d, const Vector2 &p_vec) const; diff --git a/core/math/math_funcs.cpp b/core/math/math_funcs.cpp index f060a8e4ab..5c8512d8bd 100644 --- a/core/math/math_funcs.cpp +++ b/core/math/math_funcs.cpp @@ -177,18 +177,3 @@ float Math::random(float from, float to) { float ret = (float)r / (float)RANDOM_MAX; return (ret) * (to - from) + from; } - -int Math::wrapi(int value, int min, int max) { - --max; - int rng = max - min + 1; - value = ((value - min) % rng); - if (value < 0) - return max + 1 + value; - else - return min + value; -} - -float Math::wrapf(float value, float min, float max) { - float rng = max - min; - return min + (value - min) - (rng * floor((value - min) / rng)); -} diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index e15abc6b50..26e87f009b 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -209,8 +209,18 @@ public: static _ALWAYS_INLINE_ double round(double p_val) { return (p_val >= 0) ? Math::floor(p_val + 0.5) : -Math::floor(-p_val + 0.5); } static _ALWAYS_INLINE_ float round(float p_val) { return (p_val >= 0) ? Math::floor(p_val + 0.5) : -Math::floor(-p_val + 0.5); } - static int wrapi(int value, int min, int max); - static float wrapf(float value, float min, float max); + static _ALWAYS_INLINE_ int wrapi(int value, int min, int max) { + int rng = max - min; + return min + ((((value - min) % rng) + rng) % rng); + } + static _ALWAYS_INLINE_ double wrapf(double value, double min, double max) { + double rng = max - min; + return min + (value - min) - (rng * Math::floor((value - min) / rng)); + } + static _ALWAYS_INLINE_ float wrapf(float value, float min, float max) { + float rng = max - min; + return min + (value - min) - (rng * Math::floor((value - min) / rng)); + } // double only, as these functions are mainly used by the editor and not performance-critical, static double ease(double p_x, double p_c); diff --git a/core/os/dir_access.cpp b/core/os/dir_access.cpp index 1941b82602..330a9153ef 100644 --- a/core/os/dir_access.cpp +++ b/core/os/dir_access.cpp @@ -301,8 +301,8 @@ Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) { FileAccess *fsrc = FileAccess::open(p_from, FileAccess::READ, &err); if (err) { - - ERR_FAIL_COND_V(err, err); + ERR_PRINTS("Failed to open " + p_from); + return err; } FileAccess *fdst = FileAccess::open(p_to, FileAccess::WRITE, &err); @@ -310,7 +310,8 @@ Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) { fsrc->close(); memdelete(fsrc); - ERR_FAIL_COND_V(err, err); + ERR_PRINTS("Failed to open " + p_to); + return err; } fsrc->seek_end(0); diff --git a/core/os/file_access.h b/core/os/file_access.h index 5d10c1a9aa..c4635fdfbb 100644 --- a/core/os/file_access.h +++ b/core/os/file_access.h @@ -89,6 +89,9 @@ public: virtual void close() = 0; ///< close a file virtual bool is_open() const = 0; ///< true when file is open + virtual String get_path() const { return ""; } /// returns the path for the current open file + virtual String get_path_absolute() const { return ""; } /// returns the absolute path for the current open file + virtual void seek(size_t p_position) = 0; ///< seek to a given position virtual void seek_end(int64_t p_position = 0) = 0; ///< seek from the end of file virtual size_t get_position() const = 0; ///< get position in the file diff --git a/core/os/os.h b/core/os/os.h index f8033c2546..5a2c998782 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -46,7 +46,8 @@ enum VideoDriver { VIDEO_DRIVER_GLES3, - VIDEO_DRIVER_GLES2 + VIDEO_DRIVER_GLES2, + VIDEO_DRIVER_MAX, }; class OS { diff --git a/core/project_settings.cpp b/core/project_settings.cpp index 427fa77e62..3eb8ad7bf5 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -692,7 +692,10 @@ Error ProjectSettings::_save_settings_text(const String &p_file, const Map<Strin String vstr; VariantWriter::write_to_string(value, vstr); - file->store_string(F->get() + "=" + vstr + "\n"); + if (F->get().find(" ") != -1) + file->store_string(F->get().quote() + "=" + vstr + "\n"); + else + file->store_string(F->get() + "=" + vstr + "\n"); } } diff --git a/core/script_language.h b/core/script_language.h index d1da0a3b72..6d32fc054c 100644 --- a/core/script_language.h +++ b/core/script_language.h @@ -203,6 +203,7 @@ public: virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) {} virtual bool is_using_templates() { return false; } virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL) const = 0; + virtual String validate_path(const String &p_path) const { return ""; } virtual Script *create_script() const = 0; virtual bool has_named_classes() const = 0; virtual bool supports_builtin_mode() const = 0; diff --git a/core/variant_call.cpp b/core/variant_call.cpp index d1d45f0e7c..cda7dccf0c 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -347,7 +347,7 @@ struct _VariantCall { VCALL_LOCALMEM1R(Vector2, bounce); VCALL_LOCALMEM1R(Vector2, reflect); VCALL_LOCALMEM0R(Vector2, angle); - //VCALL_LOCALMEM1R(Vector2,cross); + VCALL_LOCALMEM1R(Vector2, cross); VCALL_LOCALMEM0R(Vector2, abs); VCALL_LOCALMEM1R(Vector2, clamped); @@ -1517,7 +1517,7 @@ void register_variant_methods() { ADDFUNC1R(VECTOR2, VECTOR2, Vector2, slide, VECTOR2, "n", varray()); ADDFUNC1R(VECTOR2, VECTOR2, Vector2, bounce, VECTOR2, "n", varray()); ADDFUNC1R(VECTOR2, VECTOR2, Vector2, reflect, VECTOR2, "n", varray()); - //ADDFUNC1R(VECTOR2,REAL,Vector2,cross,VECTOR2,"with",varray()); + ADDFUNC1R(VECTOR2, REAL, Vector2, cross, VECTOR2, "with", varray()); ADDFUNC0R(VECTOR2, VECTOR2, Vector2, abs, varray()); ADDFUNC1R(VECTOR2, VECTOR2, Vector2, clamped, REAL, "length", varray()); diff --git a/core/variant_op.cpp b/core/variant_op.cpp index 97b469861c..842f5f0af6 100644 --- a/core/variant_op.cpp +++ b/core/variant_op.cpp @@ -177,7 +177,7 @@ bool Variant::booleanize() const { CASE_TYPE(m_prefix, m_op_name, m_name) { \ if (p_b.type == INT) _RETURN(p_a._data.m_type m_op p_b._data._int); \ if (p_b.type == REAL) _RETURN(p_a._data.m_type m_op p_b._data._real); \ - if (p_b.type == NIL) _RETURN(!p_b.type m_op NIL); \ + if (p_b.type == NIL) _RETURN(!(p_b.type m_op NIL)); \ \ _RETURN_FAIL \ }; @@ -252,7 +252,7 @@ bool Variant::booleanize() const { CASE_TYPE(m_prefix, m_op_name, m_name) { \ if (p_b.type == STRING) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const String *>(p_b._data._mem)); \ if (p_b.type == NODE_PATH) _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const NodePath *>(p_b._data._mem)); \ - if (p_b.type == NIL) _RETURN(!p_b.type m_op NIL); \ + if (p_b.type == NIL) _RETURN(!(p_b.type m_op NIL)); \ \ _RETURN_FAIL \ }; @@ -278,7 +278,7 @@ bool Variant::booleanize() const { if (p_b.type == m_name) \ _RETURN(*reinterpret_cast<const m_type *>(p_a._data._mem) m_op *reinterpret_cast<const m_type *>(p_b._data._mem)); \ if (p_b.type == NIL) \ - _RETURN(!p_b.type m_op NIL); \ + _RETURN(!(p_b.type m_op NIL)); \ \ _RETURN_FAIL \ }; @@ -323,7 +323,7 @@ bool Variant::booleanize() const { if (p_b.type == m_name) \ _RETURN(*p_a._data.m_sub m_op *p_b._data.m_sub); \ if (p_b.type == NIL) \ - _RETURN(!p_b.type m_op NIL); \ + _RETURN(!(p_b.type m_op NIL)); \ \ _RETURN_FAIL \ } diff --git a/doc/classes/DynamicFontData.xml b/doc/classes/DynamicFontData.xml index 510f19dd78..7b34d02316 100644 --- a/doc/classes/DynamicFontData.xml +++ b/doc/classes/DynamicFontData.xml @@ -16,7 +16,19 @@ <member name="font_path" type="String" setter="set_font_path" getter="get_font_path"> The path to the vector font file. </member> + <member name="hinting" type="int" setter="set_hinting" getter="get_hinting" enum="DynamicFontData.Hinting"> + The font hinting mode used by FreeType. + </member> </members> <constants> + <constant name="HINTING_NONE" value="0" enum="Hinting"> + Disable font hinting (smoother but less crisp). + </constant> + <constant name="HINTING_LIGHT" value="1" enum="Hinting"> + Use the light font hinting mode. + </constant> + <constant name="HINTING_NORMAL" value="2" enum="Hinting"> + Use the default font hinting mode (crisper but less smooth). + </constant> </constants> </class> diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml index 048c90e515..18adaa645c 100644 --- a/doc/classes/Environment.xml +++ b/doc/classes/Environment.xml @@ -4,8 +4,9 @@ Resource for environment nodes (like [WorldEnvironment]) that define multiple rendering options. </brief_description> <description> - Resource for environment nodes (like [WorldEnvironment]) that define multiple environment operations (such as background [Sky] or [Color], ambient light, fog, depth-of-field...). These parameters affect the final render of the scene. The order of these operations is: - - DOF Blur + Resource for environment nodes (like [WorldEnvironment]) that define multiple environment operations (such as background [Sky] or [Color], ambient light, fog, depth-of-field...). These parameters affect the final render of the scene. The order of these operations is: + + - DOF Blur - Motion Blur - Bloom - Tonemap (auto exposure) @@ -13,7 +14,7 @@ </description> <tutorials> http://docs.godotengine.org/en/3.0/tutorials/3d/environment_and_post_processing.html - http://docs.godotengine.org/en/3.0/tutorials/3d/high_dynamic_range.html + http://docs.godotengine.org/en/3.0/tutorials/3d/high_dynamic_range.html </tutorials> <demos> </demos> diff --git a/doc/classes/File.xml b/doc/classes/File.xml index 3cafb6ba3b..534323348e 100644 --- a/doc/classes/File.xml +++ b/doc/classes/File.xml @@ -163,6 +163,20 @@ Returns a [String] saved in Pascal format from the file. </description> </method> + <method name="get_path"> + <return type="String"> + </return> + <description> + Returns the path as a [String] for the current open file. + </description> + </method> + <method name="get_path_absolute"> + <return type="String"> + </return> + <description> + Returns the absolute path as a [String] for the current open file. + </description> + </method> <method name="get_position" qualifiers="const"> <return type="int"> </return> diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index b158154c49..c1bd8d34eb 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -436,74 +436,109 @@ <constant name="FORMAT_LA8" value="1" enum="Format"> </constant> <constant name="FORMAT_R8" value="2" enum="Format"> + OpenGL texture format RED with a single component and a bitdepth of 8. </constant> <constant name="FORMAT_RG8" value="3" enum="Format"> + OpenGL texture format RG with two components and a bitdepth of 8 for each. </constant> <constant name="FORMAT_RGB8" value="4" enum="Format"> + OpenGL texture format RGB with three components, each with a bitdepth of 8. </constant> <constant name="FORMAT_RGBA8" value="5" enum="Format"> + OpenGL texture format RGBA with four components, each with a bitdepth of 8. </constant> <constant name="FORMAT_RGBA4444" value="6" enum="Format"> + OpenGL texture format RGBA with four components, each with a bitdepth of 4. </constant> <constant name="FORMAT_RGBA5551" value="7" enum="Format"> + OpenGL texture format GL_RGB5_A1 where 5 bits of depth for each component of RGB and one bit for alpha. </constant> <constant name="FORMAT_RF" value="8" enum="Format"> + OpenGL texture format GL_R32F where there's one component, a 32-bit floating-point value. </constant> <constant name="FORMAT_RGF" value="9" enum="Format"> + OpenGL texture format GL_RG32F where there are two components, each a 32-bit floating-point values. </constant> <constant name="FORMAT_RGBF" value="10" enum="Format"> + OpenGL texture format GL_RGB32F where there are three components, each a 32-bit floating-point values. </constant> <constant name="FORMAT_RGBAF" value="11" enum="Format"> + OpenGL texture format GL_RGBA32F where there are four components, each a 32-bit floating-point values. </constant> <constant name="FORMAT_RH" value="12" enum="Format"> + OpenGL texture format GL_R32F where there's one component, a 16-bit "half-precision" floating-point value. </constant> <constant name="FORMAT_RGH" value="13" enum="Format"> + OpenGL texture format GL_RG32F where there's two components, each a 16-bit "half-precision" floating-point value. </constant> <constant name="FORMAT_RGBH" value="14" enum="Format"> + OpenGL texture format GL_RGB32F where there's three components, each a 16-bit "half-precision" floating-point value. </constant> <constant name="FORMAT_RGBAH" value="15" enum="Format"> + OpenGL texture format GL_RGBA32F where there's four components, each a 16-bit "half-precision" floating-point value. </constant> <constant name="FORMAT_RGBE9995" value="16" enum="Format"> + A special OpenGL texture format where the three color components have 9 bits of precision and all three share a single exponent. </constant> <constant name="FORMAT_DXT1" value="17" enum="Format"> + The S3TC texture format that uses Block Compression 1, and is the smallest variation of S3TC, only providing 1 bit of alpha and color data being premultiplied with alpha. More information can be found at https://www.khronos.org/opengl/wiki/S3_Texture_Compression. </constant> <constant name="FORMAT_DXT3" value="18" enum="Format"> + The S3TC texture format that uses Block Compression 2, and color data is interpreted as not having been premultiplied by alpha. Well suited for images with sharp alpha transitions between translucent and opaque areas. </constant> <constant name="FORMAT_DXT5" value="19" enum="Format"> + The S3TC texture format also known as Block Compression 3 or BC3 that contains 64 bits of alpha channel data followed by 64 bits of DXT1-encoded color data. Color data is not premultiplied by alpha, same as DXT3. DXT5 generally produces superior results for transparency gradients than DXT3. </constant> <constant name="FORMAT_RGTC_R" value="20" enum="Format"> + Texture format that uses Red Green Texture Compression, normalizing the red channel data using the same compression algorithm that DXT5 uses for the alpha channel. More information can be found here https://www.khronos.org/opengl/wiki/Red_Green_Texture_Compression. </constant> <constant name="FORMAT_RGTC_RG" value="21" enum="Format"> + Texture format that uses Red Green Texture Compression, normalizing the red and green channel data using the same compression algorithm that DXT5 uses for the alpha channel. </constant> <constant name="FORMAT_BPTC_RGBA" value="22" enum="Format"> + Texture format that uses BPTC compression with unsigned normalized RGBA components. More information can be found at https://www.khronos.org/opengl/wiki/BPTC_Texture_Compression. </constant> <constant name="FORMAT_BPTC_RGBF" value="23" enum="Format"> + Texture format that uses BPTC compression with signed floating-point RGB components. </constant> <constant name="FORMAT_BPTC_RGBFU" value="24" enum="Format"> + Texture format that uses BPTC compression with unsigned floating-point RGB components. </constant> <constant name="FORMAT_PVRTC2" value="25" enum="Format"> + Texture format used on PowerVR-supported mobile platforms, uses 2 bit color depth with no alpha. More information on PVRTC can be found here https://en.wikipedia.org/wiki/PVRTC. </constant> <constant name="FORMAT_PVRTC2A" value="26" enum="Format"> + Same as PVRTC2, but with an alpha component. </constant> <constant name="FORMAT_PVRTC4" value="27" enum="Format"> + Similar to PVRTC2, but with 4 bit color depth and no alpha. </constant> <constant name="FORMAT_PVRTC4A" value="28" enum="Format"> + Same as PVRTC4, but with an alpha component. </constant> <constant name="FORMAT_ETC" value="29" enum="Format"> + Ericsson Texture Compression format, also referred to as 'ETC1', and is part of the OpenGL ES graphics standard. An overview of the format is given at https://en.wikipedia.org/wiki/Ericsson_Texture_Compression#ETC1. </constant> <constant name="FORMAT_ETC2_R11" value="30" enum="Format"> + Ericsson Texture Compression format 2 variant R11_EAC, which provides one channel of unsigned data. </constant> <constant name="FORMAT_ETC2_R11S" value="31" enum="Format"> + Ericsson Texture Compression format 2 variant SIGNED_R11_EAC, which provides one channel of signed data. </constant> <constant name="FORMAT_ETC2_RG11" value="32" enum="Format"> + Ericsson Texture Compression format 2 variant RG11_EAC, which provides two channels of unsigned data. </constant> <constant name="FORMAT_ETC2_RG11S" value="33" enum="Format"> + Ericsson Texture Compression format 2 variant SIGNED_RG11_EAC, which provides two channels of signed data. </constant> <constant name="FORMAT_ETC2_RGB8" value="34" enum="Format"> + Ericsson Texture Compression format 2 variant RGB8, which is a followup of ETC1 and compresses RGB888 data. </constant> <constant name="FORMAT_ETC2_RGBA8" value="35" enum="Format"> + Ericsson Texture Compression format 2 variant RGBA8, which compresses RGBA8888 data with full alpha support. </constant> <constant name="FORMAT_ETC2_RGB8A1" value="36" enum="Format"> + Ericsson Texture Compression format 2 variant RGB8_PUNCHTHROUGH_ALPHA1, which compresses RGBA data to make alpha either fully transparent or fully opaque. </constant> <constant name="FORMAT_MAX" value="37" enum="Format"> </constant> diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index a7a6c2ef45..5b2e019c4c 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -7,6 +7,7 @@ A Singleton that deals with inputs. This includes key presses, mouse buttons and movement, joypads, and input actions. Actions and their events can be set in the Project Settings / Input Map tab. Or be set with [InputMap]. </description> <tutorials> + http://docs.godotengine.org/en/3.0/tutorials/inputs/index.html </tutorials> <demos> </demos> @@ -44,7 +45,7 @@ <return type="Vector3"> </return> <description> - If the device has an accelerometer, this will return the movement. + If the device has an accelerometer, this will return the acceleration. Otherwise, it returns an empty [Vector3]. </description> </method> <method name="get_connected_joypads"> @@ -58,13 +59,14 @@ <return type="Vector3"> </return> <description> + If the device has an accelerometer, this will return the gravity. Otherwise, it returns an empty [Vector3]. </description> </method> <method name="get_gyroscope" qualifiers="const"> <return type="Vector3"> </return> <description> - If the device has a gyroscope, this will return the rate of rotation in rad/s around a device's x, y, and z axis. + If the device has a gyroscope, this will return the rate of rotation in rad/s around a device's x, y, and z axis. Otherwise, it returns an empty [Vector3]. </description> </method> <method name="get_joy_axis" qualifiers="const"> @@ -259,6 +261,7 @@ <argument index="0" name="event" type="InputEvent"> </argument> <description> + Feeds an [InputEvent] to the game. Can be used to artificially trigger input events from code. </description> </method> <method name="remove_joy_mapping"> @@ -334,7 +337,7 @@ <argument index="1" name="connected" type="bool"> </argument> <description> - Emitted when a joypad device has been connected or disconnected + Emitted when a joypad device has been connected or disconnected. </description> </signal> </signals> @@ -349,40 +352,58 @@ Captures the mouse. The mouse will be hidden and unable to leave the game window. But it will still register movement and mouse button presses. </constant> <constant name="MOUSE_MODE_CONFINED" value="3" enum="MouseMode"> + Makes the mouse cursor visible but confines it to the game window. </constant> <constant name="CURSOR_ARROW" value="0" enum="CursorShape"> + Arrow cursor. Standard, default pointing cursor. </constant> <constant name="CURSOR_IBEAM" value="1" enum="CursorShape"> + I-beam cursor. Usually used to show where the text cursor will appear when the mouse is clicked. </constant> <constant name="CURSOR_POINTING_HAND" value="2" enum="CursorShape"> + Pointing hand cursor. Usually used to indicate the pointer is over a link or other interactable item. </constant> <constant name="CURSOR_CROSS" value="3" enum="CursorShape"> + Cross cursor. Typically appears over regions in which a drawing operation can be performance or for selections. </constant> <constant name="CURSOR_WAIT" value="4" enum="CursorShape"> + Wait cursor. Indicates that the application is busy performing an operation. </constant> <constant name="CURSOR_BUSY" value="5" enum="CursorShape"> + Busy cursor. See [code]CURSOR_WAIT[/code]. </constant> <constant name="CURSOR_DRAG" value="6" enum="CursorShape"> + Drag cursor. Usually displayed when dragging something. </constant> <constant name="CURSOR_CAN_DROP" value="7" enum="CursorShape"> + Can drop cursor. Usually displayed when dragging something to indicate that it can be dropped at the current position. </constant> <constant name="CURSOR_FORBIDDEN" value="8" enum="CursorShape"> + Forbidden cursor. Indicates that the current action is forbidden (for example, when dragging something) or that the control at a position is disabled. </constant> <constant name="CURSOR_VSIZE" value="9" enum="CursorShape"> + Vertical resize mouse cursor. A double headed vertical arrow. It tells the user they can resize the window or the panel vertically. </constant> <constant name="CURSOR_HSIZE" value="10" enum="CursorShape"> + Horizontal resize mouse cursor. A double headed horizontal arrow. It tells the user they can resize the window or the panel horizontally. </constant> <constant name="CURSOR_BDIAGSIZE" value="11" enum="CursorShape"> + Window resize mouse cursor. The cursor is a double headed arrow that goes from the bottom left to the top right. It tells the user they can resize the window or the panel both horizontally and vertically. </constant> <constant name="CURSOR_FDIAGSIZE" value="12" enum="CursorShape"> + Window resize mouse cursor. The cursor is a double headed arrow that goes from the top left to the bottom right, the opposite of [code]CURSOR_BDIAGSIZE[/code]. It tells the user they can resize the window or the panel both horizontally and vertically. </constant> <constant name="CURSOR_MOVE" value="13" enum="CursorShape"> + Move cursor. Indicates that something can be moved. </constant> <constant name="CURSOR_VSPLIT" value="14" enum="CursorShape"> + Vertical split mouse cursor. On Windows, it's the same as [code]CURSOR_VSIZE[/code]. </constant> <constant name="CURSOR_HSPLIT" value="15" enum="CursorShape"> + Horizontal split mouse cursor. On Windows, it's the same as [code]CURSOR_HSIZE[/code]. </constant> <constant name="CURSOR_HELP" value="16" enum="CursorShape"> + Help cursor. Usually a question mark. </constant> </constants> </class> diff --git a/doc/classes/Navigation2D.xml b/doc/classes/Navigation2D.xml index 6f9ff7a2be..d2f4513372 100644 --- a/doc/classes/Navigation2D.xml +++ b/doc/classes/Navigation2D.xml @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="Navigation2D" inherits="Node2D" category="Core" version="3.1"> <brief_description> + Class to assist with character navigation and pathfinding. </brief_description> <description> </description> diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 8d07aa95f3..3f9a9cd0c8 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -13,7 +13,8 @@ Nodes can also process input events. When present, the [method _input] function will be called for each input that the program receives. In many cases, this can be overkill (unless used for simple projects), and the [method _unhandled_input] function might be preferred; it is called when the input event was not handled by anyone else (typically, GUI [Control] nodes), ensuring that the node only receives the events that were meant for it. To keep track of the scene hierarchy (especially when instancing scenes into other scenes), an "owner" can be set for the node with [method set_owner]. This keeps track of who instanced what. This is mostly useful when writing editors and tools, though. Finally, when a node is freed with [method free] or [method queue_free], it will also free all its children. - [b]Networking with nodes:[/b] After connecting to a server (or making one, see [NetworkedMultiplayerENet]) it is possible to use the built-in RPC (remote procedure call) system to communicate over the network. By calling [method rpc] with a method name, it will be called locally and in all connected peers (peers = clients and the server that accepts connections), with behaviour varying depending on the network mode ([method set_network_mode]) of the receiving peer. To identify which node receives the RPC call Godot will use its [NodePath] (make sure node names are the same on all peers). + [b]Groups:[/b] Nodes can be added to as many groups as you want to be easy to manage, you could create groups like "enemies" or "collectables" for example, depending on your game. See [method add_to_group], [method is_in_group] and [method remove_from_group]. You can then retrieve all nodes in these groups, iterate them and even call methods on groups via the methods on [SceneTree]. + [b]Networking with nodes:[/b] After connecting to a server (or making one, see [NetworkedMultiplayerENet]) it is possible to use the built-in RPC (remote procedure call) system to communicate over the network. By calling [method rpc] with a method name, it will be called locally and in all connected peers (peers = clients and the server that accepts connections). To identify which node receives the RPC call Godot will use its [NodePath] (make sure node names are the same on all peers). Also take a look at the high-level networking tutorial and corresponding demos. </description> <tutorials> http://docs.godotengine.org/en/3.0/getting_started/step_by_step/scenes_and_nodes.html @@ -45,6 +46,8 @@ <description> Called when there is an input event. The input event propagates through the node tree until a node consumes it. It is only called if input processing is enabled, which is done automatically if this method is overridden, and can be toggled with [method set_process_input]. + To consume the input event and stop it propagating further to other nodes, [method SceneTree.set_input_as_handled] can be called. + For gameplay input, [method _unhandled_input] and [method _unhandled_key_input] are usually a better fit as they allow the GUI to intercept the events first. </description> </method> <method name="_physics_process" qualifiers="virtual"> @@ -74,7 +77,8 @@ </return> <description> Called when the node is "ready", i.e. when both the node and its children have entered the scene tree. If the node has children, their [method _ready] callbacks get triggered first, and the parent node will receive the ready notification afterwards. - Corresponds to the NOTIFICATION_READY notification in [method Object._notification]. + Corresponds to the NOTIFICATION_READY notification in [method Object._notification]. See also the [code]onready[/code] keyword for variables. + Usually used for initialization. For even earlier initialization, [method Object._init] may be used. Also see [method _enter_tree]. </description> </method> <method name="_unhandled_input" qualifiers="virtual"> @@ -83,8 +87,10 @@ <argument index="0" name="event" type="InputEvent"> </argument> <description> - Propagated to all nodes when the previous InputEvent is not consumed by any nodes. + Propagated to all nodes when the previous [InputEvent] is not consumed by any nodes. It is only called if unhandled input processing is enabled, which is done automatically if this method is overridden, and can be toggled with [method set_process_unhandled_input]. + To consume the input event and stop it propagating further to other nodes, [method SceneTree.set_input_as_handled] can be called. + For gameplay input, this and [method _unhandled_key_input] are usually a better fit than [method _input] as they allow the GUI to intercept the events first. </description> </method> <method name="_unhandled_key_input" qualifiers="virtual"> @@ -93,6 +99,10 @@ <argument index="0" name="event" type="InputEventKey"> </argument> <description> + Propagated to all nodes when the previous [InputEventKey] is not consumed by any nodes. + It is only called if unhandled key input processing is enabled, which is done automatically if this method is overridden, and can be toggled with [method set_process_unhandled_key_input]. + To consume the input event and stop it propagating further to other nodes, [method SceneTree.set_input_as_handled] can be called. + For gameplay input, this and [method _unhandled_input] are usually a better fit than [method _input] as they allow the GUI to intercept the events first. </description> </method> <method name="add_child"> @@ -129,7 +139,7 @@ <argument index="1" name="persistent" type="bool" default="false"> </argument> <description> - Adds the node to a group. Groups are helpers to name and organize a subset of nodes, for example "enemies" or "collectables". A node can be in any number of groups. Nodes can be assigned a group at any time, but will not be added until they are inside the scene tree (see [method is_inside_tree]). + Adds the node to a group. Groups are helpers to name and organize a subset of nodes, for example "enemies" or "collectables". A node can be in any number of groups. Nodes can be assigned a group at any time, but will not be added until they are inside the scene tree (see [method is_inside_tree]). See notes in the description, and the group methods in [SceneTree]. </description> </method> <method name="can_process" qualifiers="const"> @@ -203,7 +213,7 @@ <return type="int"> </return> <description> - Returns the peer ID of the network master for this node. + Returns the peer ID of the network master for this node. See [method set_network_master]. </description> </method> <method name="get_node" qualifiers="const"> @@ -262,7 +272,7 @@ <argument index="0" name="node" type="Node"> </argument> <description> - Returns the relative path from the current node to the specified node in "node" argument. Both nodes must be in the same scene, or the function will fail. + Returns the relative [NodePath] from this node to the specified [code]node[/code]. Both nodes must be in the same scene or the function will fail. </description> </method> <method name="get_physics_process_delta_time" qualifiers="const"> @@ -290,6 +300,7 @@ <return type="bool"> </return> <description> + Returns [code]true[/code] if this is an instance load placeholder. See [InstancePlaceholder]. </description> </method> <method name="get_tree" qualifiers="const"> @@ -354,7 +365,7 @@ <argument index="0" name="group" type="String"> </argument> <description> - Returns [code]true[/code] if this node is in the specified group. + Returns [code]true[/code] if this node is in the specified group. See notes in the description, and the group methods in [SceneTree]. </description> </method> <method name="is_inside_tree" qualifiers="const"> @@ -368,6 +379,7 @@ <return type="bool"> </return> <description> + Returns [code]true[/code] if the local system is the master of this node. </description> </method> <method name="is_physics_processing" qualifiers="const"> @@ -381,6 +393,7 @@ <return type="bool"> </return> <description> + Returns [code]true[/code] if internal physics processing is enabled (see [method set_physics_process_internal]). </description> </method> <method name="is_processing" qualifiers="const"> @@ -401,6 +414,7 @@ <return type="bool"> </return> <description> + Returns [code]true[/code] if internal processing is enabled (see [method set_process_internal]). </description> </method> <method name="is_processing_unhandled_input" qualifiers="const"> @@ -432,13 +446,14 @@ <return type="void"> </return> <description> + Prints all stray nodes (nodes outside the [SceneTree]). Used for debugging. Works only in debug builds. </description> </method> <method name="print_tree"> <return type="void"> </return> <description> - Prints the scene to stdout. Used mainly for debugging purposes. + Prints the scene hierarchy of this node and all it's children to stdout. Used mainly for debugging purposes. </description> </method> <method name="propagate_call"> @@ -499,7 +514,7 @@ <argument index="0" name="group" type="String"> </argument> <description> - Removes a node from a group. + Removes a node from a group. See notes in the description, and the group methods in [SceneTree]. </description> </method> <method name="replace_by"> @@ -526,7 +541,7 @@ <argument index="0" name="method" type="String"> </argument> <description> - Sends a remote procedure call request to all peers on the network (and locally), optionally sending additional data as arguments. Call request will be received by nodes with the same [NodePath]. + Sends a remote procedure call request for the given [code]method[/code] to peers on the network (and locally), optionally sending all additional arguments as arguments to the method called by the RPC. The call request will only be received by nodes with the same [NodePath], including the exact same node name. Behaviour depends on the RPC configuration for the given method, see [method rpc_config]. Methods are not exposed to RPCs by default. Also see [method rset] and [method rset_config] for properties. Returns an empty [Variant]. Note that you can only safely use RPCs on clients after you received the [code]connected_to_server[/code] signal from the [SceneTree]. You also need to keep track of the connection state, either by the [SceneTree] signals like [code]server_disconnected[/code] or by checking [code]SceneTree.network_peer.get_connection_status() == CONNECTION_CONNECTED[/code]. </description> </method> <method name="rpc_config"> @@ -537,7 +552,7 @@ <argument index="1" name="mode" type="int" enum="Node.RPCMode"> </argument> <description> - Changes the method's RPC mode (one of RPC_MODE_* constants). + Changes the RPC mode for the given [code]method[/code] to the given [code]mode[/code]. See [enum RPCMode]. An alternative is annotating methods and properties with the corresponding keywords ([code]remote[/code], [code]sync[/code], [code]master[/code], [code]slave[/code]). By default, methods are not exposed to networking (and RPCs). Also see [method rset] and [method rset_config] for properties. </description> </method> <method name="rpc_id" qualifiers="vararg"> @@ -548,7 +563,7 @@ <argument index="1" name="method" type="String"> </argument> <description> - Sends a [method rpc] to a specific peer identified by [i]peer_id[/i]. + Sends a [method rpc] to a specific peer identified by [code]peer_id[/code]. Returns an empty [Variant]. </description> </method> <method name="rpc_unreliable" qualifiers="vararg"> @@ -557,7 +572,7 @@ <argument index="0" name="method" type="String"> </argument> <description> - Sends a [method rpc] using an unreliable protocol. + Sends a [method rpc] using an unreliable protocol. Returns an empty [Variant]. </description> </method> <method name="rpc_unreliable_id" qualifiers="vararg"> @@ -568,7 +583,7 @@ <argument index="1" name="method" type="String"> </argument> <description> - Sends a [method rpc] to a specific peer identified by [i]peer_id[/i] using an unreliable protocol. + Sends a [method rpc] to a specific peer identified by [code]peer_id[/code] using an unreliable protocol. Returns an empty [Variant]. </description> </method> <method name="rset"> @@ -579,7 +594,7 @@ <argument index="1" name="value" type="Variant"> </argument> <description> - Remotely changes property's value on other peers (and locally). + Remotely changes a property's value on other peers (and locally). Behaviour depends on the RPC configuration for the given property, see [method rset_config]. Also see [method rpc] for RPCs for methods, most information applies to this method as well. </description> </method> <method name="rset_config"> @@ -590,7 +605,7 @@ <argument index="1" name="mode" type="int" enum="Node.RPCMode"> </argument> <description> - Changes the property's RPC mode (one of RPC_MODE_* constants). + Changes the RPC mode for the given [code]property[/code] to the given [code]mode[/code]. See [enum RPCMode]. An alternative is annotating methods and properties with the corresponding keywords ([code]remote[/code], [code]sync[/code], [code]master[/code], [code]slave[/code]). By default, properties are not exposed to networking (and RPCs). Also see [method rpc] and [method rpc_config] for methods. </description> </method> <method name="rset_id"> @@ -603,7 +618,7 @@ <argument index="2" name="value" type="Variant"> </argument> <description> - Remotely changes property's value on a specific peer identified by [i]peer_id[/i]. + Remotely changes the property's value on a specific peer identified by [code]peer_id[/code]. </description> </method> <method name="rset_unreliable"> @@ -614,7 +629,7 @@ <argument index="1" name="value" type="Variant"> </argument> <description> - Remotely changes property's value on other peers (and locally) using an unreliable protocol. + Remotely changes the property's value on other peers (and locally) using an unreliable protocol. </description> </method> <method name="rset_unreliable_id"> @@ -627,7 +642,7 @@ <argument index="2" name="value" type="Variant"> </argument> <description> - Remotely changes property's value on a specific peer identified by [i]peer_id[/i] using an unreliable protocol. + Remotely changes property's value on a specific peer identified by [code]peer_id[/code] using an unreliable protocol. </description> </method> <method name="set_display_folded"> @@ -647,7 +662,7 @@ <argument index="1" name="recursive" type="bool" default="true"> </argument> <description> - Sets the node network master to the peer with the given peer ID. The network master is the peer that has authority over it on the network. Inherited from the parent node by default, which ultimately defaults to peer ID 1 (the server). + Sets the node's network master to the peer with the given peer ID. The network master is the peer that has authority over the node on the network. Useful in conjunction with the [code]master[/code] and [code]slave[/code] keywords. Inherited from the parent node by default, which ultimately defaults to peer ID 1 (the server). If [code]recursive[/code], the given peer is recursively set as the master for all children of this node. </description> </method> <method name="set_physics_process"> @@ -665,6 +680,7 @@ <argument index="0" name="enable" type="bool"> </argument> <description> + Enables or disables internal physics for this node. Internal physics processing happens in isolation from the normal [method]_physics_process[/code] calls and is used by some nodes internally to guarantee proper functioning even if the node is paused or physics processing is disabled for scripting ([method set_physics_process]). Only useful for advanced uses to manipulate built-in nodes behaviour. </description> </method> <method name="set_process"> @@ -691,6 +707,7 @@ <argument index="0" name="enable" type="bool"> </argument> <description> + Enables or disabled internal processing for this node. Internal processing happens in isolation from the normal [method]_process[/code] calls and is used by some nodes internally to guarantee proper functioning even if the node is paused or processing is disabled for scripting ([method set_process]). Only useful for advanced uses to manipulate built-in nodes behaviour. </description> </method> <method name="set_process_unhandled_input"> @@ -717,6 +734,7 @@ <argument index="0" name="load_placeholder" type="bool"> </argument> <description> + Sets whether this is an instance load placeholder. See [InstancePlaceholder]. </description> </method> </methods> @@ -725,13 +743,13 @@ When a scene is instanced from a file, its topmost node contains the filename from which it was loaded. </member> <member name="name" type="String" setter="set_name" getter="get_name"> - The name of the node. This name is unique among the siblings (other child nodes from the same parent). - When set to an existing name, the node will be automatically renamed + The name of the node. This name is unique among the siblings (other child nodes from the same parent). When set to an existing name, the node will be automatically renamed </member> <member name="owner" type="Node" setter="set_owner" getter="get_owner"> The node owner. A node can have any other node as owner (as long as it is a valid parent, grandparent, etc. ascending in the tree). When saving a node (using SceneSaver) all the nodes it owns will be saved with it. This allows for the creation of complex [SceneTree]s, with instancing and subinstancing. </member> <member name="pause_mode" type="int" setter="set_pause_mode" getter="get_pause_mode" enum="Node.PauseMode"> + Pause mode. How the node will behave if the [SceneTree] is paused. </member> </members> <signals> @@ -752,7 +770,7 @@ </signal> <signal name="tree_exiting"> <description> - Emitted when the node is still active but about to exit the tree. This is the right place for de-initialization. + Emitted when the node is still active but about to exit the tree. This is the right place for de-initialization (or a "destructor", if you will). </description> </signal> </signals> @@ -800,33 +818,37 @@ Notification received when the node's [NodePath] changed. </constant> <constant name="NOTIFICATION_TRANSLATION_CHANGED" value="24"> + Notification received when translations may have changed. Can be triggered by the user changing the locale. Can be used to respond to language changes, for example to change the UI strings on the fly. Useful when working with the built-in translation support, like [method Object.tr]. </constant> <constant name="NOTIFICATION_INTERNAL_PROCESS" value="25"> + Notification received every frame when the internal process flag is set (see [method set_process_internal]). </constant> <constant name="NOTIFICATION_INTERNAL_PHYSICS_PROCESS" value="26"> + Notification received every frame when the internal physics process flag is set (see [method set_physics_process_internal]). </constant> <constant name="RPC_MODE_DISABLED" value="0" enum="RPCMode"> + Used with [method rpc_config] or [method rset_config] to disable a method or property for all RPC calls, making it unavailable. Default for all methods. </constant> <constant name="RPC_MODE_REMOTE" value="1" enum="RPCMode"> - Call a method remotely. + Used with [method rpc_config] or [method rset_config] to set a method to be called or a property to be changed only on the remote end, not locally. Analogous to the [code]remote[/code] keyword. </constant> <constant name="RPC_MODE_SYNC" value="2" enum="RPCMode"> - Call a method both remotely and locally. + Used with [method rpc_config] or [method rset_config] to set a method to be called or a property to be changed both on the remote end and locally. Analogous to the [code]sync[/code] keyword. </constant> <constant name="RPC_MODE_MASTER" value="3" enum="RPCMode"> - Call a method if the Node is Master. + Used with [method rpc_config] or [method rset_config] to set a method to be called or a property to be changed only on the network master for this node. Analogous to the [code]master[/code] keyword. See [method set_network_master]. </constant> <constant name="RPC_MODE_SLAVE" value="4" enum="RPCMode"> - Call a method if the Node is Slave. + Used with [method rpc_config] or [method rset_config] to set a method to be called or a property to be changed only on slaves for this node. Analogous to the [code]slave[/code] keyword. See [method set_network_master]. </constant> <constant name="PAUSE_MODE_INHERIT" value="0" enum="PauseMode"> - Inherits pause mode from parent. For root node, it is equivalent to PAUSE_MODE_STOP. + Inherits pause mode from the node's parent. For the root node, it is equivalent to PAUSE_MODE_STOP. Default. </constant> <constant name="PAUSE_MODE_STOP" value="1" enum="PauseMode"> - Stop processing when SceneTree is paused. + Stop processing when the [SceneTree] is paused. </constant> <constant name="PAUSE_MODE_PROCESS" value="2" enum="PauseMode"> - Continue to process regardless of SceneTree pause state. + Continue to process regardless of the [SceneTree] pause state. </constant> <constant name="DUPLICATE_SIGNALS" value="1" enum="DuplicateFlags"> Duplicate the node's signals. diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index 237b5f62d4..6ee9489995 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -19,6 +19,13 @@ Clear all cells. </description> </method> + <method name="fix_invalid_tiles"> + <return type="void"> + </return> + <description> + Clear cells that does not exist in the tileset. + </description> + </method> <method name="get_cell" qualifiers="const"> <return type="int"> </return> diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml index 9df2eb143b..b293ec3368 100644 --- a/doc/classes/Vector2.xml +++ b/doc/classes/Vector2.xml @@ -81,6 +81,15 @@ Returns the vector with a maximum length. </description> </method> + <method name="cross"> + <return type="float"> + </return> + <argument index="0" name="b" type="Vector2"> + </argument> + <description> + Returns the 2-dimensional analog of the cross product with [code]b[/code]. + </description> + </method> <method name="cubic_interpolate"> <return type="Vector2"> </return> diff --git a/doc/tools/makemd.py b/doc/tools/makemd.py index a73a4337d0..056f1ca82d 100644 --- a/doc/tools/makemd.py +++ b/doc/tools/makemd.py @@ -93,6 +93,8 @@ def make_class_list(class_list, columns): s += '\n' f.write(s) + f.close() + def dokuize_text(txt): @@ -330,6 +332,8 @@ def make_doku_class(node): f.write('\n') f.write(dokuize_text(d.text.strip())) f.write('\n') + + f.close() for file in input_list: diff --git a/doc/tools/makerst.py b/doc/tools/makerst.py index 9e767bf3d6..8074ce45ea 100644 --- a/doc/tools/makerst.py +++ b/doc/tools/makerst.py @@ -104,6 +104,7 @@ def make_class_list(class_list, columns): f.write("--+-------+") f.write("\n") + f.close() def rstize_text(text, cclass): # Linebreak + tabs in the XML should become two line breaks unless in a "codeblock" @@ -572,6 +573,8 @@ def make_rst_class(node): f.write("\n\n") f.write('\n') + f.close() + file_list = [] diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 3732fe77b1..ca39d9f966 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -415,4 +415,5 @@ RasterizerGLES3::~RasterizerGLES3() { memdelete(storage); memdelete(canvas); + memdelete(scene); } diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 5bb332816d..02d851288a 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -5118,3 +5118,23 @@ void RasterizerSceneGLES3::finalize() { RasterizerSceneGLES3::RasterizerSceneGLES3() { } + +RasterizerSceneGLES3::~RasterizerSceneGLES3() { + + memdelete(default_material.get_data()); + memdelete(default_material_twosided.get_data()); + memdelete(default_shader.get_data()); + memdelete(default_shader_twosided.get_data()); + + memdelete(default_worldcoord_material.get_data()); + memdelete(default_worldcoord_material_twosided.get_data()); + memdelete(default_worldcoord_shader.get_data()); + memdelete(default_worldcoord_shader_twosided.get_data()); + + memdelete(default_overdraw_material.get_data()); + memdelete(default_overdraw_shader.get_data()); + + memfree(state.spot_array_tmp); + memfree(state.omni_array_tmp); + memfree(state.reflection_array_tmp); +} diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index 1b99e119ce..a6faeef473 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -852,6 +852,7 @@ public: void initialize(); void finalize(); RasterizerSceneGLES3(); + ~RasterizerSceneGLES3(); }; #endif // RASTERIZERSCENEGLES3_H diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp index e90245b300..9639f06345 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp +++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp @@ -156,38 +156,50 @@ Error AudioDriverPulseAudio::init() { exit_thread = false; mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE); - channels = detect_channels(); - switch (channels) { + // Detect the amount of channels PulseAudio is using + // Note: If using an even amount of channels (2, 4, etc) channels and pa_channels will be equal, + // if not then pa_channels will have the real amount of channels PulseAudio is using and channels + // will have the amount of channels Godot is using (in this case it's pa_channels + 1) + pa_channels = detect_channels(); + switch (pa_channels) { + case 1: // Mono + case 3: // Surround 2.1 + case 5: // Surround 5.0 + case 7: // Surround 7.0 + channels = pa_channels + 1; + break; + case 2: // Stereo - case 4: // Surround 3.1 + case 4: // Surround 4.0 case 6: // Surround 5.1 case 8: // Surround 7.1 + channels = pa_channels; break; default: - ERR_PRINTS("PulseAudio: Unsupported number of channels: " + itos(channels)); + ERR_PRINTS("PulseAudio: Unsupported number of channels: " + itos(pa_channels)); ERR_FAIL_V(ERR_CANT_OPEN); break; } pa_sample_spec spec; spec.format = PA_SAMPLE_S16LE; - spec.channels = channels; + spec.channels = pa_channels; spec.rate = mix_rate; int latency = GLOBAL_DEF("audio/output_latency", DEFAULT_OUTPUT_LATENCY); buffer_frames = closest_power_of_2(latency * mix_rate / 1000); - buffer_size = buffer_frames * channels; + pa_buffer_size = buffer_frames * pa_channels; if (OS::get_singleton()->is_stdout_verbose()) { - print_line("PulseAudio: detected " + itos(channels) + " channels"); + print_line("PulseAudio: detected " + itos(pa_channels) + " channels"); print_line("PulseAudio: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms"); } pa_buffer_attr attr; // set to appropriate buffer length (in bytes) from global settings - attr.tlength = buffer_size * sizeof(int16_t); + attr.tlength = pa_buffer_size * sizeof(int16_t); // set them to be automatically chosen attr.prebuf = (uint32_t)-1; attr.maxlength = (uint32_t)-1; @@ -209,8 +221,8 @@ Error AudioDriverPulseAudio::init() { ERR_FAIL_COND_V(pulse == NULL, ERR_CANT_OPEN); } - samples_in.resize(buffer_size); - samples_out.resize(buffer_size); + samples_in.resize(buffer_frames * channels); + samples_out.resize(pa_buffer_size); mutex = Mutex::create(); thread = Thread::create(AudioDriverPulseAudio::thread_func, this); @@ -235,7 +247,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { while (!ad->exit_thread) { if (!ad->active) { - for (unsigned int i = 0; i < ad->buffer_size; i++) { + for (unsigned int i = 0; i < ad->pa_buffer_size; i++) { ad->samples_out[i] = 0; } @@ -246,15 +258,30 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { ad->unlock(); - for (unsigned int i = 0; i < ad->buffer_size; i++) { - ad->samples_out[i] = ad->samples_in[i] >> 16; + if (ad->channels == ad->pa_channels) { + for (unsigned int i = 0; i < ad->pa_buffer_size; i++) { + ad->samples_out[i] = ad->samples_in[i] >> 16; + } + } else { + // Uneven amount of channels + unsigned int in_idx = 0; + unsigned int out_idx = 0; + + for (unsigned int i = 0; i < ad->buffer_frames; i++) { + for (unsigned int j = 0; j < ad->pa_channels - 1; j++) { + ad->samples_out[out_idx++] = ad->samples_in[in_idx++] >> 16; + } + uint32_t l = ad->samples_in[in_idx++]; + uint32_t r = ad->samples_in[in_idx++]; + ad->samples_out[out_idx++] = (l >> 1 + r >> 1) >> 16; + } } } // pa_simple_write always consumes the entire buffer int error_code; - int byte_size = ad->buffer_size * sizeof(int16_t); + int byte_size = ad->pa_buffer_size * sizeof(int16_t); if (pa_simple_write(ad->pulse, ad->samples_out.ptr(), byte_size, &error_code) < 0) { // can't recover here fprintf(stderr, "PulseAudio failed and can't recover: %s\n", pa_strerror(error_code)); @@ -328,17 +355,16 @@ AudioDriverPulseAudio::AudioDriverPulseAudio() { samples_out.clear(); mix_rate = 0; - buffer_size = 0; + buffer_frames = 0; + pa_buffer_size = 0; channels = 0; + pa_channels = 0; active = false; thread_exited = false; exit_thread = false; latency = 0; - buffer_frames = 0; - buffer_size = 0; - channels = 0; } AudioDriverPulseAudio::~AudioDriverPulseAudio() { diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.h b/drivers/pulseaudio/audio_driver_pulseaudio.h index 3bd1146f53..5737f24314 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.h +++ b/drivers/pulseaudio/audio_driver_pulseaudio.h @@ -51,8 +51,9 @@ class AudioDriverPulseAudio : public AudioDriver { unsigned int mix_rate; unsigned int buffer_frames; - unsigned int buffer_size; + unsigned int pa_buffer_size; int channels; + int pa_channels; bool active; bool thread_exited; diff --git a/drivers/unix/dir_access_unix.cpp b/drivers/unix/dir_access_unix.cpp index 3944431516..5a4be6df4f 100644 --- a/drivers/unix/dir_access_unix.cpp +++ b/drivers/unix/dir_access_unix.cpp @@ -244,7 +244,7 @@ static void _get_drives(List<String> *list) { // Parse only file:// links if (strncmp(string, "file://", 7) == 0) { // Strip any unwanted edges on the strings and push_back if it's not a duplicate - String fpath = String(string + 7).strip_edges(); + String fpath = String(string + 7).strip_edges().split_spaces()[0].percent_decode(); if (!list->find(fpath)) { list->push_back(fpath); } diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp index 5b093a5885..a7a3eef935 100644 --- a/drivers/unix/file_access_unix.cpp +++ b/drivers/unix/file_access_unix.cpp @@ -69,6 +69,7 @@ Error FileAccessUnix::_open(const String &p_path, int p_mode_flags) { fclose(f); f = NULL; + path_src = p_path; path = fix_path(p_path); //printf("opening %ls, %i\n", path.c_str(), Memory::get_static_mem_usage()); @@ -152,6 +153,16 @@ bool FileAccessUnix::is_open() const { return (f != NULL); } +String FileAccessUnix::get_path() const { + + return path_src; +} + +String FileAccessUnix::get_path_absolute() const { + + return path; +} + void FileAccessUnix::seek(size_t p_position) { ERR_FAIL_COND(!f); diff --git a/drivers/unix/file_access_unix.h b/drivers/unix/file_access_unix.h index dbb1c9f3b5..88bb39fbd1 100644 --- a/drivers/unix/file_access_unix.h +++ b/drivers/unix/file_access_unix.h @@ -51,6 +51,7 @@ class FileAccessUnix : public FileAccess { mutable Error last_error; String save_path; String path; + String path_src; static FileAccess *create_libc(); @@ -61,6 +62,9 @@ public: virtual void close(); ///< close a file virtual bool is_open() const; ///< true when file is open + virtual String get_path() const; /// returns the path for the current open file + virtual String get_path_absolute() const; /// returns the absolute path for the current open file + virtual void seek(size_t p_position); ///< seek to a given position virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file virtual size_t get_position() const; ///< get position in the file diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index 072790876f..e10f4d05e8 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -57,7 +57,8 @@ void FileAccessWindows::check_errors() const { Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { - String filename = fix_path(p_path); + path_src = p_path; + path = fix_path(p_path); if (f) close(); @@ -78,19 +79,19 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { backend supports utf8 encoding */ struct _stat st; - if (_wstat(filename.c_str(), &st) == 0) { + if (_wstat(path.c_str(), &st) == 0) { if (!S_ISREG(st.st_mode)) return ERR_FILE_CANT_OPEN; }; if (is_backup_save_enabled() && p_mode_flags & WRITE && !(p_mode_flags & READ)) { - save_path = filename; - filename = filename + ".tmp"; + save_path = path; + path = path + ".tmp"; //print_line("saving instead to "+path); } - f = _wfopen(filename.c_str(), mode_string); + f = _wfopen(path.c_str(), mode_string); if (f == NULL) { last_error = ERR_FILE_CANT_OPEN; @@ -154,6 +155,17 @@ void FileAccessWindows::close() { ERR_FAIL_COND(rename_error); } } + +String FileAccessWindows::get_path() const { + + return path_src; +} + +String FileAccessWindows::get_path_absolute() const { + + return path; +} + bool FileAccessWindows::is_open() const { return (f != NULL); diff --git a/drivers/windows/file_access_windows.h b/drivers/windows/file_access_windows.h index 26bd08b7af..0462c1e942 100644 --- a/drivers/windows/file_access_windows.h +++ b/drivers/windows/file_access_windows.h @@ -46,6 +46,8 @@ class FileAccessWindows : public FileAccess { int flags; void check_errors() const; mutable Error last_error; + String path; + String path_src; String save_path; public: @@ -53,6 +55,9 @@ public: virtual void close(); ///< close a file virtual bool is_open() const; ///< true when file is open + virtual String get_path() const; /// returns the path for the current open file + virtual String get_path_absolute() const; /// returns the absolute path for the current open file + virtual void seek(size_t p_position); ///< seek to a given position virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file virtual size_t get_position() const; ///< get position in the file diff --git a/editor/SCsub b/editor/SCsub index 8f87e12cb9..4ca6b9e3fd 100644 --- a/editor/SCsub +++ b/editor/SCsub @@ -4,6 +4,7 @@ Import('env') env.editor_sources = [] import os +import os.path from compat import encode_utf8, byte_to_str, open_utf8, escape_string @@ -29,6 +30,9 @@ def make_certs_header(target, source, env): g.write("};\n") g.write("#endif") + g.close() + f.close() + def make_doc_header(target, source, env): @@ -41,8 +45,8 @@ def make_doc_header(target, source, env): src = s.srcnode().abspath if not src.endswith(".xml"): continue - f = open_utf8(src, "r") - content = f.read() + with open_utf8(src, "r") as f: + content = f.read() buf += content buf = encode_utf8(docbegin + buf + docend) @@ -62,6 +66,8 @@ def make_doc_header(target, source, env): g.write("#endif") + g.close() + def make_fonts_header(target, source, env): @@ -76,9 +82,8 @@ def make_fonts_header(target, source, env): # saving uncompressed, since freetype will reference from memory pointer xl_names = [] for i in range(len(source)): - f = open(source[i].srcnode().abspath, "rb") - buf = f.read() - import os.path + with open(source[i].srcnode().abspath, "rb")as f: + buf = f.read() name = os.path.splitext(os.path.basename(source[i].srcnode().abspath))[0] @@ -91,6 +96,8 @@ def make_fonts_header(target, source, env): g.write("#endif") + g.close() + def make_translations_header(target, source, env): @@ -110,8 +117,8 @@ def make_translations_header(target, source, env): xl_names = [] for i in range(len(sorted_paths)): - f = open(sorted_paths[i], "rb") - buf = f.read() + with open(sorted_paths[i], "rb") as f: + buf = f.read() decomp_size = len(buf) buf = zlib.compress(buf) name = os.path.splitext(os.path.basename(sorted_paths[i]))[0] @@ -138,6 +145,9 @@ def make_translations_header(target, source, env): g.write("#endif") + g.close() + + def make_authors_header(target, source, env): sections = ["Project Founders", "Lead Developer", "Project Manager", "Developers"] @@ -180,6 +190,9 @@ def make_authors_header(target, source, env): g.write("#endif\n") + g.close() + f.close() + def make_donors_header(target, source, env): sections = ["Platinum sponsors", "Gold sponsors", "Mini sponsors", "Gold donors", "Silver donors", "Bronze donors"] @@ -222,6 +235,10 @@ def make_donors_header(target, source, env): g.write("#endif\n") + g.close() + f.close() + + def make_license_header(target, source, env): src_copyright = source[0].srcnode().abspath @@ -387,17 +404,23 @@ def make_license_header(target, source, env): g.write("#endif\n") + g.close() + fc.close() + f.close() + def _make_doc_data_class_path(to_path): - g = open_utf8(os.path.join(to_path,"doc_data_class_path.gen.h"), "w") - g.write("static const int _doc_data_class_path_count = " + str(len(env.doc_class_path)) + ";\n") - g.write("struct _DocDataClassPath { const char* name; const char* path; };\n") + g = open_utf8(os.path.join(to_path,"doc_data_class_path.gen.h"), "w") + g.write("static const int _doc_data_class_path_count = " + str(len(env.doc_class_path)) + ";\n") + g.write("struct _DocDataClassPath { const char* name; const char* path; };\n") + + g.write("static const _DocDataClassPath _doc_data_class_paths[" + str(len(env.doc_class_path) + 1) + "] = {\n"); + for c in sorted(env.doc_class_path): + g.write("\t{\"" + c + "\", \"" + env.doc_class_path[c] + "\"},\n") + g.write("\t{NULL, NULL}\n") + g.write("};\n") - g.write("static const _DocDataClassPath _doc_data_class_paths[" + str(len(env.doc_class_path) + 1) + "] = {\n"); - for c in sorted(env.doc_class_path): - g.write("\t{\"" + c + "\", \"" + env.doc_class_path[c] + "\"},\n") - g.write("\t{NULL, NULL}\n") - g.write("};\n") + g.close() if env['tools']: @@ -409,10 +432,9 @@ if env['tools']: reg_exporters += '\tregister_' + e + '_exporter();\n' reg_exporters_inc += '#include "platform/' + e + '/export/export.h"\n' reg_exporters += '}\n' - f = open_utf8("register_exporters.gen.cpp", "w") - f.write(reg_exporters_inc) - f.write(reg_exporters) - f.close() + with open_utf8("register_exporters.gen.cpp", "w") as f: + f.write(reg_exporters_inc) + f.write(reg_exporters) # API documentation docs = [] @@ -466,6 +488,7 @@ if env['tools']: env.add_source_files(env.editor_sources, "*.cpp") + env.add_source_files(env.editor_sources, ["#thirdparty/misc/clipper.cpp"]) SConscript('collada/SCsub') SConscript('doc/SCsub') diff --git a/editor/animation_editor.cpp b/editor/animation_editor.cpp index 1b97a24968..439ec37e71 100644 --- a/editor/animation_editor.cpp +++ b/editor/animation_editor.cpp @@ -1149,14 +1149,12 @@ void AnimationKeyEditor::_track_editor_draw() { get_icon("KeyCall", "EditorIcons") }; + Ref<Texture> valid_icon = get_icon("KeyValid", "EditorIcons"); Ref<Texture> invalid_icon = get_icon("KeyInvalid", "EditorIcons"); - Ref<Texture> invalid_icon_hover = get_icon("KeyInvalidHover", "EditorIcons"); + const Color modulate_selected = Color(0x84 / 255.0, 0xc2 / 255.0, 0xff / 255.0); Ref<Texture> hsize_icon = get_icon("Hsize", "EditorIcons"); - Ref<Texture> type_hover = get_icon("KeyHover", "EditorIcons"); - Ref<Texture> type_selected = get_icon("KeySelected", "EditorIcons"); - int right_separator_ofs = right_data_size_cache; int h = font->get_height() + sep; @@ -1268,8 +1266,21 @@ void AnimationKeyEditor::_track_editor_draw() { int decimals = 2; bool step_found = false; + const int period_width = font->get_char_size('.').width; + int max_digit_width = font->get_char_size('0').width; + for (int i = 1; i <= 9; i++) { + const int digit_width = font->get_char_size('0' + i).width; + max_digit_width = MAX(digit_width, max_digit_width); + } + const int max_sc = int(Math::ceil(zoomw / scale)); + const int max_sc_width = String::num(max_sc).length() * max_digit_width; + while (!step_found) { + min = max_sc_width; + if (decimals > 0) + min += period_width + max_digit_width * decimals; + static const int _multp[3] = { 1, 2, 5 }; for (int i = 0; i < 3; i++) { @@ -1464,6 +1475,10 @@ void AnimationKeyEditor::_track_editor_draw() { float x = key_hofs + name_limit + (time - keys_from) * zoom_scale; Ref<Texture> tex = type_icon[tt]; + Color modulate = Color(1, 1, 1); + + bool is_hover = false; + bool is_selected = false; SelectedKey sk; sk.key = i; @@ -1472,13 +1487,33 @@ void AnimationKeyEditor::_track_editor_draw() { if (click.click == ClickOver::CLICK_MOVE_KEYS) continue; - tex = type_selected; + is_selected = true; } if (mouse_over.over == MouseOver::OVER_KEY && mouse_over.track == idx && mouse_over.over_key == i) - tex = type_hover; + is_hover = true; Variant value = animation->track_get_key_value(idx, i); + + if (prop_exists && !Variant::can_convert(value.get_type(), valid_type)) { + + tex = invalid_icon; + if (is_hover) + modulate = Color(1.5, 1.5, 1.5); + else + modulate = Color(1, 1, 1); + } else if (is_selected) { + + tex = valid_icon; + modulate = modulate_selected; + if (is_hover) + modulate = modulate.lightened(0.2); + } else if (is_hover) { + + tex = valid_icon; + modulate = Color(1, 1, 1); + } + if (first && i > 0 && value == animation->track_get_key_value(idx, i - 1)) { te->draw_line(ofs + Vector2(name_limit, y + h / 2), ofs + Point2(x, y + h / 2), color); @@ -1491,19 +1526,7 @@ void AnimationKeyEditor::_track_editor_draw() { te->draw_line(ofs + Point2(x_n, y + h / 2), ofs + Point2(x, y + h / 2), color); } - if (prop_exists && !Variant::can_convert(value.get_type(), valid_type)) { - te->draw_texture(invalid_icon, ofs + Point2(x, y + key_vofs).floor()); - } - - if (prop_exists && !Variant::can_convert(value.get_type(), valid_type)) { - if (tex == type_hover) - te->draw_texture(invalid_icon_hover, ofs + Point2(x, y + key_vofs).floor()); - else - te->draw_texture(invalid_icon, ofs + Point2(x, y + key_vofs).floor()); - } else { - - te->draw_texture(tex, ofs + Point2(x, y + key_vofs).floor()); - } + te->draw_texture(tex, ofs + Point2(x, y + key_vofs).floor(), modulate); first = false; } @@ -1539,8 +1562,8 @@ void AnimationKeyEditor::_track_editor_draw() { continue; int y = h + i * h + sep; - float key_vofs = Math::floor((float)(h - type_selected->get_height()) / 2); - float key_hofs = -Math::floor((float)type_selected->get_height() / 2); + float key_vofs = Math::floor((float)(h - valid_icon->get_height()) / 2); + float key_hofs = -Math::floor((float)valid_icon->get_height() / 2); float time = animation->track_get_key_time(idx, E->key().key); float diff = time - from_t; @@ -1554,7 +1577,7 @@ void AnimationKeyEditor::_track_editor_draw() { x += name_limit; - te->draw_texture(type_selected, ofs + Point2(x + key_hofs, y + key_vofs).floor()); + te->draw_texture(valid_icon, ofs + Point2(x + key_hofs, y + key_vofs).floor(), modulate_selected); } } break; default: {}; @@ -2930,8 +2953,8 @@ void AnimationKeyEditor::_track_editor_gui_input(const Ref<InputEvent> &p_input) Ref<InputEventPanGesture> pan_gesture = p_input; if (pan_gesture.is_valid()) { - h_scroll->set_value(h_scroll->get_value() - h_scroll->get_page() * pan_gesture->get_delta().x / 8); - v_scroll->set_value(v_scroll->get_value() - v_scroll->get_page() * pan_gesture->get_delta().y / 8); + h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * pan_gesture->get_delta().x / 8); + v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * pan_gesture->get_delta().y / 8); } } @@ -3998,6 +4021,7 @@ AnimationKeyEditor::AnimationKeyEditor() { //key_edit->ke_dialog=key_edit_dialog; type_menu = memnew(PopupMenu); + type_menu->set_pass_on_modal_close_click(false); add_child(type_menu); for (int i = 0; i < Variant::VARIANT_MAX; i++) type_menu->add_item(Variant::get_type_name(Variant::Type(i)), i); @@ -4038,6 +4062,7 @@ AnimationKeyEditor::AnimationKeyEditor() { add_child(track_name); track_name->connect("text_entered", this, "_track_name_changed"); track_menu = memnew(PopupMenu); + track_menu->set_pass_on_modal_close_click(false); add_child(track_menu); track_menu->connect("id_pressed", this, "_track_menu_selected"); diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp index 9ab539da07..360ed620f6 100644 --- a/editor/editor_about.cpp +++ b/editor/editor_about.cpp @@ -44,9 +44,11 @@ void EditorAbout::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - Ref<Font> font = EditorNode::get_singleton()->get_gui_base()->get_font("source", "EditorFonts"); + Control *base = EditorNode::get_singleton()->get_gui_base(); + Ref<Font> font = base->get_font("source", "EditorFonts"); _tpl_text->add_font_override("normal_font", font); _license_text->add_font_override("normal_font", font); + _logo->set_texture(base->get_icon("Logo", "EditorIcons")); } break; } } diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp index c57d65d321..ac4402d50b 100644 --- a/editor/editor_audio_buses.cpp +++ b/editor/editor_audio_buses.cpp @@ -121,6 +121,26 @@ void EditorAudioBus::_notification(int p_what) { set_process(is_visible_in_tree()); } + + if (p_what == NOTIFICATION_THEME_CHANGED) { + + for (int i = 0; i < cc; i++) { + channel[i].vu_l->set_under_texture(get_icon("BusVuEmpty", "EditorIcons")); + channel[i].vu_l->set_progress_texture(get_icon("BusVuFull", "EditorIcons")); + channel[i].vu_r->set_under_texture(get_icon("BusVuEmpty", "EditorIcons")); + channel[i].vu_r->set_progress_texture(get_icon("BusVuFull", "EditorIcons")); + channel[i].prev_active = true; + } + scale->set_texture(get_icon("BusVuDb", "EditorIcons")); + + disabled_vu = get_icon("BusVuFrozen", "EditorIcons"); + + solo->set_icon(get_icon("AudioBusSolo", "EditorIcons")); + mute->set_icon(get_icon("AudioBusMute", "EditorIcons")); + bypass->set_icon(get_icon("AudioBusBypass", "EditorIcons")); + + bus_options->set_icon(get_icon("GuiMiniTabMenu", "EditorIcons")); + } } void EditorAudioBus::update_send() { @@ -719,6 +739,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) { effects->set_hide_root(true); effects->set_custom_minimum_size(Size2(0, 100) * EDSCALE); effects->set_hide_folding(true); + effects->set_v_size_flags(SIZE_EXPAND_FILL); vb->add_child(effects); effects->connect("item_edited", this, "_effect_edited"); effects->connect("cell_selected", this, "_effect_selected"); @@ -1151,6 +1172,7 @@ EditorAudioBuses::EditorAudioBuses() { bus_scroll->set_enable_v_scroll(false); add_child(bus_scroll); bus_hb = memnew(HBoxContainer); + bus_hb->set_v_size_flags(SIZE_EXPAND_FILL); bus_scroll->add_child(bus_hb); save_timer = memnew(Timer); diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp index 0e9b7b74fd..a2f5c1aa1a 100644 --- a/editor/editor_autoload_settings.cpp +++ b/editor/editor_autoload_settings.cpp @@ -643,10 +643,10 @@ EditorAutoloadSettings::EditorAutoloadSettings() { tree->set_column_title(2, TTR("Singleton")); tree->set_column_expand(2, false); - tree->set_column_min_width(2, 80); + tree->set_column_min_width(2, 80 * EDSCALE); tree->set_column_expand(3, false); - tree->set_column_min_width(3, 120); + tree->set_column_min_width(3, 120 * EDSCALE); tree->connect("cell_selected", this, "_autoload_selected"); tree->connect("item_edited", this, "_autoload_edited"); diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp index da4bbf9b7a..7456cc902a 100644 --- a/editor/editor_export.cpp +++ b/editor/editor_export.cpp @@ -1352,27 +1352,24 @@ Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_pr DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); Error err = da->copy(template_path, p_path, get_chmod_flags()); - memdelete(da); - - if (err != OK) { - return err; - } - - String pck_path = p_path.get_basename() + ".pck"; + if (err == OK) { + String pck_path = p_path.get_basename() + ".pck"; - Vector<SharedObject> so_files; + Vector<SharedObject> so_files; - err = save_pack(p_preset, pck_path, &so_files); + err = save_pack(p_preset, pck_path, &so_files); - if (err != OK || so_files.empty()) - return err; - //if shared object files, copy them - da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - for (int i = 0; i < so_files.size(); i++) { - da->copy(so_files[i].path, p_path.get_base_dir().plus_file(so_files[i].path.get_file())); + if (err == OK && !so_files.empty()) { + //if shared object files, copy them + da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + for (int i = 0; i < so_files.size() && err == OK; i++) { + err = da->copy(so_files[i].path, p_path.get_base_dir().plus_file(so_files[i].path.get_file())); + } + } } + memdelete(da); - return OK; + return err; } void EditorExportPlatformPC::set_extension(const String &p_extension, const String &p_feature_key) { diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index dca32d7492..d8ae1da72e 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -275,9 +275,13 @@ void EditorFileSystem::_scan_filesystem() { memdelete(d); f = FileAccess::open(fscache, FileAccess::WRITE); - _save_filesystem_cache(new_filesystem, f); - f->close(); - memdelete(f); + if (f == NULL) { + ERR_PRINTS("Error writing fscache: " + fscache); + } else { + _save_filesystem_cache(new_filesystem, f); + f->close(); + memdelete(f); + } scanning = false; } @@ -286,9 +290,13 @@ void EditorFileSystem::_save_filesystem_cache() { String fscache = EditorSettings::get_singleton()->get_project_settings_dir().plus_file("filesystem_cache3"); FileAccess *f = FileAccess::open(fscache, FileAccess::WRITE); - _save_filesystem_cache(filesystem, f); - f->close(); - memdelete(f); + if (f == NULL) { + ERR_PRINTS("Error writing fscache: " + fscache); + } else { + _save_filesystem_cache(filesystem, f); + f->close(); + memdelete(f); + } } void EditorFileSystem::_thread_func(void *_userdata) { @@ -321,10 +329,10 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo List<String> to_check; - String source_file; - String source_md5; + String source_file = ""; + String source_md5 = ""; Vector<String> dest_files; - String dest_md5; + String dest_md5 = ""; while (true) { @@ -334,7 +342,6 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, NULL, true); if (err == ERR_FILE_EOF) { - memdelete(f); break; } else if (err != OK) { ERR_PRINTS("ResourceFormatImporter::load - " + p_path + ".import:" + itos(lines) + " error: " + error_text); @@ -351,12 +358,8 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo to_check.push_back(fa[i]); } } else if (!p_only_imported_files) { - if (assign == "source_md5") { - source_md5 = value; - } else if (assign == "source_file") { + if (assign == "source_file") { source_file = value; - } else if (assign == "dest_md5") { - dest_md5 = value; } else if (assign == "dest_files") { dest_files = value; } @@ -369,6 +372,42 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo memdelete(f); + // Read the md5's from a separate file (so the import parameters aren't dependant on the file version + String base_path = ResourceFormatImporter::get_singleton()->get_import_base_path(p_path); + FileAccess *md5s = FileAccess::open(base_path + ".md5", FileAccess::READ, &err); + if (!md5s) { // No md5's stored for this resource + return true; + } + + VariantParser::StreamFile md5_stream; + md5_stream.f = md5s; + + while (true) { + assign = Variant(); + next_tag.fields.clear(); + next_tag.name = String(); + + err = VariantParser::parse_tag_assign_eof(&md5_stream, lines, error_text, next_tag, assign, value, NULL, true); + + if (err == ERR_FILE_EOF) { + break; + } else if (err != OK) { + ERR_PRINTS("ResourceFormatImporter::load - " + p_path + ".import.md5:" + itos(lines) + " error: " + error_text); + memdelete(md5s); + return false; // parse error + } + if (assign != String()) { + if (!p_only_imported_files) { + if (assign == "source_md5") { + source_md5 = value; + } else if (assign == "dest_md5") { + dest_md5 = value; + } + } + } + } + memdelete(md5s); + //imported files are gone, reimport for (List<String>::Element *E = to_check.front(); E; E = E->next()) { if (!FileAccess::exists(E->get())) { @@ -1316,6 +1355,9 @@ void EditorFileSystem::update_file(const String &p_file) { fs->files[cpos]->deps = _get_dependencies(p_file); fs->files[cpos]->import_valid = ResourceLoader::is_import_valid(p_file); + // Update preview + EditorResourcePreview::get_singleton()->check_for_invalidation(p_file); + call_deferred("emit_signal", "filesystem_changed"); //update later } @@ -1456,15 +1498,13 @@ void EditorFileSystem::_reimport_file(const String &p_file) { } f->store_line("source_file=" + Variant(p_file).get_construct_string()); - f->store_line("source_md5=\"" + FileAccess::get_md5(p_file) + "\"\n"); if (dest_paths.size()) { Array dp; for (int i = 0; i < dest_paths.size(); i++) { dp.push_back(dest_paths[i]); } - f->store_line("dest_files=" + Variant(dp).get_construct_string()); - f->store_line("dest_md5=\"" + FileAccess::get_multiple_md5(dest_paths) + "\"\n"); + f->store_line("dest_files=" + Variant(dp).get_construct_string() + "\n"); } f->store_line("[params]"); @@ -1483,6 +1523,16 @@ void EditorFileSystem::_reimport_file(const String &p_file) { f->close(); memdelete(f); + // Store the md5's of the various files. These are stored separately so that the .import files can be version controlled. + FileAccess *md5s = FileAccess::open(base_path + ".md5", FileAccess::WRITE); + ERR_FAIL_COND(!md5s); + md5s->store_line("source_md5=\"" + FileAccess::get_md5(p_file) + "\""); + if (dest_paths.size()) { + md5s->store_line("dest_md5=\"" + FileAccess::get_multiple_md5(dest_paths) + "\"\n"); + } + md5s->close(); + memdelete(md5s); + //update modified times, to avoid reimport fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file); fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(p_file + ".import"); diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp index a58257962a..2ec3cdb08f 100644 --- a/editor/editor_fonts.cpp +++ b/editor/editor_fonts.cpp @@ -103,9 +103,11 @@ void editor_register_fonts(Ref<Theme> p_theme) { /* Custom font */ String custom_font = EditorSettings::get_singleton()->get("interface/editor/main_font"); + DynamicFontData::Hinting font_hinting = (DynamicFontData::Hinting)(int)EditorSettings::get_singleton()->get("interface/editor/main_font_hinting"); Ref<DynamicFontData> CustomFont; if (custom_font.length() > 0) { CustomFont.instance(); + CustomFont->set_hinting(font_hinting); CustomFont->set_font_path(custom_font); CustomFont->set_force_autohinter(true); //just looks better..i think? } @@ -113,9 +115,11 @@ void editor_register_fonts(Ref<Theme> p_theme) { /* Custom source code font */ String custom_font_source = EditorSettings::get_singleton()->get("interface/editor/code_font"); + DynamicFontData::Hinting font_source_hinting = (DynamicFontData::Hinting)(int)EditorSettings::get_singleton()->get("interface/editor/code_font_hinting"); Ref<DynamicFontData> CustomFontSource; if (custom_font_source.length() > 0) { CustomFontSource.instance(); + CustomFontSource->set_hinting(font_source_hinting); CustomFontSource->set_font_path(custom_font_source); } @@ -123,38 +127,45 @@ void editor_register_fonts(Ref<Theme> p_theme) { Ref<DynamicFontData> DefaultFont; DefaultFont.instance(); + DefaultFont->set_hinting(font_hinting); DefaultFont->set_font_ptr(_font_NotoSansUI_Regular, _font_NotoSansUI_Regular_size); DefaultFont->set_force_autohinter(true); //just looks better..i think? Ref<DynamicFontData> FontFallback; FontFallback.instance(); + FontFallback->set_hinting(font_hinting); FontFallback->set_font_ptr(_font_DroidSansFallback, _font_DroidSansFallback_size); FontFallback->set_force_autohinter(true); //just looks better..i think? Ref<DynamicFontData> FontJapanese; FontJapanese.instance(); + FontJapanese->set_hinting(font_hinting); FontJapanese->set_font_ptr(_font_DroidSansJapanese, _font_DroidSansJapanese_size); FontJapanese->set_force_autohinter(true); //just looks better..i think? Ref<DynamicFontData> FontArabic; FontArabic.instance(); + FontArabic->set_hinting(font_hinting); FontArabic->set_font_ptr(_font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size); FontArabic->set_force_autohinter(true); //just looks better..i think? Ref<DynamicFontData> FontHebrew; FontHebrew.instance(); + FontHebrew->set_hinting(font_hinting); FontHebrew->set_font_ptr(_font_NotoSansHebrew_Regular, _font_NotoSansHebrew_Regular_size); FontHebrew->set_force_autohinter(true); //just looks better..i think? Ref<DynamicFontData> FontThai; FontThai.instance(); + FontThai->set_hinting(font_hinting); FontThai->set_font_ptr(_font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size); FontThai->set_force_autohinter(true); //just looks better..i think? - /* Source Code Pro */ + /* Hack */ Ref<DynamicFontData> dfmono; dfmono.instance(); + dfmono->set_hinting(font_source_hinting); dfmono->set_font_ptr(_font_Hack_Regular, _font_Hack_Regular_size); //dfd->set_force_autohinter(true); //just looks better..i think? diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index e301d12214..158eedfb0f 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -88,6 +88,7 @@ void EditorLog::_notification(int p_what) { void EditorLog::_clear_request() { log->clear(); + tool_button->set_icon(Ref<Texture>()); } void EditorLog::clear() { @@ -103,9 +104,7 @@ void EditorLog::add_message(const String &p_msg, bool p_error) { Ref<Texture> icon = get_icon("Error", "EditorIcons"); log->add_image(icon); log->add_text(" "); - //button->set_icon(icon); - } else { - //button->set_icon(Ref<Texture>()); + tool_button->set_icon(icon); } log->add_text(p_msg); @@ -115,6 +114,10 @@ void EditorLog::add_message(const String &p_msg, bool p_error) { log->pop(); } +void EditorLog::set_tool_button(ToolButton *p_tool_button) { + tool_button = p_tool_button; +} + /* void EditorLog::_dragged(const Point2& p_ofs) { diff --git a/editor/editor_log.h b/editor/editor_log.h index 42137f71cc..f9bc82de7d 100644 --- a/editor/editor_log.h +++ b/editor/editor_log.h @@ -51,6 +51,7 @@ class EditorLog : public VBoxContainer { RichTextLabel *log; HBoxContainer *title_hb; //PaneDrag *pd; + ToolButton *tool_button; static void _error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, ErrorHandlerType p_type); @@ -68,6 +69,7 @@ protected: public: void add_message(const String &p_msg, bool p_error = false); + void set_tool_button(ToolButton *p_tool_button); void deinit(); void clear(); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 8132575479..e627263909 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -349,14 +349,20 @@ void EditorNode::_notification(int p_what) { // update_icons for (int i = 0; i < singleton->main_editor_buttons.size(); i++) { - Ref<Texture> icon = singleton->main_editor_buttons[i]->get_icon(); + + ToolButton *tb = singleton->main_editor_buttons[i]; + EditorPlugin *p_editor = singleton->editor_table[i]; + Ref<Texture> icon = p_editor->get_icon(); if (icon.is_valid()) { - main_editor_buttons[i]->set_icon(icon); - } else if (singleton->gui_base->has_icon(singleton->main_editor_buttons[i]->get_name(), "EditorIcons")) { - main_editor_buttons[i]->set_icon(gui_base->get_icon(singleton->main_editor_buttons[i]->get_name(), "EditorIcons")); + tb->set_icon(icon); + } else if (singleton->gui_base->has_icon(p_editor->get_name(), "EditorIcons")) { + tb->set_icon(singleton->gui_base->get_icon(p_editor->get_name(), "EditorIcons")); } } + + _build_icon_type_cache(); + play_button->set_icon(gui_base->get_icon("MainPlay", "EditorIcons")); play_scene_button->set_icon(gui_base->get_icon("PlayScene", "EditorIcons")); play_custom_scene_button->set_icon(gui_base->get_icon("PlayCustom", "EditorIcons")); @@ -382,6 +388,15 @@ void EditorNode::_notification(int p_what) { dock_tab_move_left->set_icon(theme->get_icon("Back", "EditorIcons")); dock_tab_move_right->set_icon(theme->get_icon("Forward", "EditorIcons")); update_menu->set_icon(gui_base->get_icon("Progress1", "EditorIcons")); + + PopupMenu *p = help_menu->get_popup(); + p->set_item_icon(p->get_item_index(HELP_CLASSES), gui_base->get_icon("ClassList", "EditorIcons")); + p->set_item_icon(p->get_item_index(HELP_SEARCH), gui_base->get_icon("HelpSearch", "EditorIcons")); + p->set_item_icon(p->get_item_index(HELP_DOCS), gui_base->get_icon("Instance", "EditorIcons")); + p->set_item_icon(p->get_item_index(HELP_QA), gui_base->get_icon("Instance", "EditorIcons")); + p->set_item_icon(p->get_item_index(HELP_ISSUES), gui_base->get_icon("Instance", "EditorIcons")); + p->set_item_icon(p->get_item_index(HELP_COMMUNITY), gui_base->get_icon("Instance", "EditorIcons")); + p->set_item_icon(p->get_item_index(HELP_ABOUT), gui_base->get_icon("Godot", "EditorIcons")); } if (p_what == Control::NOTIFICATION_RESIZED) { @@ -597,6 +612,7 @@ void EditorNode::save_resource_in_path(const Ref<Resource> &p_resource, const St Error err = ResourceSaver::save(path, p_resource, flg | ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS); if (err != OK) { + current_option = -1; accept->set_text(TTR("Error saving resource!")); accept->popup_centered_minsize(); return; @@ -983,6 +999,7 @@ void EditorNode::_save_scene_with_preview(String p_file, int p_idx) { String file = cache_base + ".png"; + post_process_preview(img); img->save_png(file); } @@ -1194,6 +1211,7 @@ void EditorNode::_dialog_action(String p_file) { Error err = ResourceSaver::save(p_file, ml); if (err) { + current_option = -1; accept->get_ok()->set_text(TTR("I see..")); accept->set_text(TTR("Error saving MeshLibrary!")); accept->popup_centered_minsize(); @@ -1228,6 +1246,7 @@ void EditorNode::_dialog_action(String p_file) { Error err = ResourceSaver::save(p_file, ml); if (err) { + current_option = -1; accept->get_ok()->set_text(TTR("I see..")); accept->set_text(TTR("Error saving TileSet!")); accept->popup_centered_minsize(); @@ -3441,6 +3460,19 @@ Ref<Texture> EditorNode::_file_dialog_get_icon(const String &p_path) { return singleton->icon_type_cache["Object"]; } +void EditorNode::_build_icon_type_cache() { + + List<StringName> tl; + StringName ei = "EditorIcons"; + theme_base->get_theme()->get_icon_list(ei, &tl); + for (List<StringName>::Element *E = tl.front(); E; E = E->next()) { + + if (!ClassDB::class_exists(E->get())) + continue; + icon_type_cache[E->get()] = theme_base->get_theme()->get_icon(E->get(), ei); + } +} + void EditorNode::_file_dialog_register(FileDialog *p_dialog) { singleton->file_dialogs.insert(p_dialog); @@ -5132,7 +5164,6 @@ EditorNode::EditorNode() { gui_base->add_child(export_template_manager); about = memnew(EditorAbout); - about->get_logo()->set_texture(gui_base->get_icon("Logo", "EditorIcons")); gui_base->add_child(about); warning = memnew(AcceptDialog); @@ -5557,7 +5588,8 @@ EditorNode::EditorNode() { bottom_panel_vb->add_child(bottom_panel_hb); log = memnew(EditorLog); - add_bottom_panel_item(TTR("Output"), log); + ToolButton *output_button = add_bottom_panel_item(TTR("Output"), log); + log->set_tool_button(output_button); old_split_ofs = 0; @@ -5797,17 +5829,7 @@ EditorNode::EditorNode() { EditorFileSystem::get_singleton()->connect("filesystem_changed", this, "_fs_changed"); EditorFileSystem::get_singleton()->connect("resources_reimported", this, "_resources_reimported"); - { - List<StringName> tl; - StringName ei = "EditorIcons"; - theme_base->get_theme()->get_icon_list(ei, &tl); - for (List<StringName>::Element *E = tl.front(); E; E = E->next()) { - - if (!ClassDB::class_exists(E->get())) - continue; - icon_type_cache[E->get()] = theme_base->get_theme()->get_icon(E->get(), ei); - } - } + _build_icon_type_cache(); Node::set_human_readable_collision_renaming(true); diff --git a/editor/editor_node.h b/editor/editor_node.h index 6d96c2dea7..9090066dea 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -496,6 +496,7 @@ private: Set<EditorFileDialog *> editor_file_dialogs; Map<String, Ref<Texture> > icon_type_cache; + void _build_icon_type_cache(); bool _initializing_addons; Map<String, EditorPlugin *> plugin_addons; diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 29363e29c1..3a75673560 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -290,6 +290,10 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { hints["interface/editor/main_font_size"] = PropertyInfo(Variant::INT, "interface/editor/main_font_size", PROPERTY_HINT_RANGE, "10,40,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); _initial_set("interface/editor/code_font_size", 14); hints["interface/editor/code_font_size"] = PropertyInfo(Variant::INT, "interface/editor/code_font_size", PROPERTY_HINT_RANGE, "8,96,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + _initial_set("interface/editor/main_font_hinting", 2); + hints["interface/editor/main_font_hinting"] = PropertyInfo(Variant::INT, "interface/editor/main_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); + _initial_set("interface/editor/code_font_hinting", 2); + hints["interface/editor/code_font_hinting"] = PropertyInfo(Variant::INT, "interface/editor/code_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); _initial_set("interface/editor/main_font", ""); hints["interface/editor/main_font"] = PropertyInfo(Variant::STRING, "interface/editor/main_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); _initial_set("interface/editor/code_font", ""); @@ -959,11 +963,8 @@ void EditorSettings::save() { Error err = ResourceSaver::save(singleton->config_file_path, singleton); if (err != OK) { - ERR_PRINT("Can't Save!"); - return; - } - - if (OS::get_singleton()->is_stdout_verbose()) { + ERR_PRINTS("Error saving editor settings to " + singleton->config_file_path); + } else if (OS::get_singleton()->is_stdout_verbose()) { print_line("EditorSettings Save OK!"); } } diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 0be3bb86c7..9bfa50148f 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -918,7 +918,7 @@ void FileSystemDock::_make_dir_confirm() { if (dir_name.length() == 0) { EditorNode::get_singleton()->show_warning(TTR("No name provided")); return; - } else if (dir_name.find("/") != -1 || dir_name.find("\\") != -1 || dir_name.find(":") != -1) { + } else if (dir_name.find("/") != -1 || dir_name.find("\\") != -1 || dir_name.find(":") != -1 || dir_name.ends_with(".")) { EditorNode::get_singleton()->show_warning(TTR("Provided name contains invalid characters")); return; } diff --git a/editor/icons/SCsub b/editor/icons/SCsub index 64992a9f90..7f94073e01 100644 --- a/editor/icons/SCsub +++ b/editor/icons/SCsub @@ -82,10 +82,9 @@ def make_editor_icons_action(target, source, env): s.write("#endif\n") + with open(dst, "w") as f: + f.write(s.getvalue()) - f = open(dst, "w") - f.write(s.getvalue()) - f.close() s.close() icons_string.close() diff --git a/editor/icons/icon_key_selected.svg b/editor/icons/icon_key_selected.svg deleted file mode 100644 index c73d31981d..0000000000 --- a/editor/icons/icon_key_selected.svg +++ /dev/null @@ -1,5 +0,0 @@ -<svg width="8" height="8" version="1.1" viewBox="0 0 8 8" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1044.4)"> -<rect transform="rotate(-45)" x="-741.53" y="741.08" width="6.1027" height="6.1027" ry=".76286" fill="#84c2ff"/> -</g> -</svg> diff --git a/editor/icons/icon_key_hover.svg b/editor/icons/icon_key_valid.svg index 4a3fab4754..4a3fab4754 100644 --- a/editor/icons/icon_key_hover.svg +++ b/editor/icons/icon_key_valid.svg diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index c22e1cd88b..04c9246aed 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -1011,6 +1011,7 @@ void AnimationPlayerEditor::_seek_value_changed(float p_value, bool p_set) { player->seek_delta(pos, pos - cpos); } else { + player->stop(true); player->seek(pos, true); } diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 4b9e5ae301..c203b4b81e 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -514,6 +514,7 @@ EditorAssetLibraryItemDownload::EditorAssetLibraryItemDownload() { download = memnew(HTTPRequest); add_child(download); download->connect("request_completed", this, "_http_download_completed"); + download->set_use_threads(EDITOR_DEF("asset_library/use_threads", true)); download_error = memnew(AcceptDialog); add_child(download_error); @@ -533,11 +534,9 @@ void EditorAssetLibrary::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { - TextureRect *tf = memnew(TextureRect); - tf->set_texture(get_icon("Error", "EditorIcons")); + error_tr->set_texture(get_icon("Error", "EditorIcons")); reverse->set_icon(get_icon("Sort", "EditorIcons")); - error_hb->add_child(tf); error_label->raise(); } break; @@ -585,6 +584,8 @@ void EditorAssetLibrary::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { library_scroll_bg->add_style_override("panel", get_stylebox("bg", "Tree")); + error_tr->set_texture(get_icon("Error", "EditorIcons")); + reverse->set_icon(get_icon("Sort", "EditorIcons")); } break; } } @@ -832,6 +833,7 @@ void EditorAssetLibrary::_request_image(ObjectID p_for, String p_image_url, Imag iq.image_index = p_image_index; iq.image_type = p_type; iq.request = memnew(HTTPRequest); + iq.request->set_use_threads(EDITOR_DEF("asset_library/use_threads", true)); iq.target = p_for; iq.queue_id = ++last_queue_id; @@ -1452,6 +1454,8 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { error_label = memnew(Label); error_label->add_color_override("color", get_color("error_color", "Editor")); error_hb->add_child(error_label); + error_tr = memnew(TextureRect); + error_hb->add_child(error_tr); description = NULL; diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h index b344716c1d..89b79d7cfb 100644 --- a/editor/plugins/asset_library_editor_plugin.h +++ b/editor/plugins/asset_library_editor_plugin.h @@ -194,6 +194,7 @@ class EditorAssetLibrary : public PanelContainer { Button *search; ProgressBar *load_status; HBoxContainer *error_hb; + TextureRect *error_tr; Label *error_label; MenuButton *support; diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index ba68259599..a502048726 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -1615,10 +1615,10 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) { if (drag_type == DRAG_NONE) { if (b.is_valid() && - ((b->get_button_index() == BUTTON_LEFT && b->get_alt() && tool == TOOL_SELECT) || + ((b->get_button_index() == BUTTON_RIGHT && b->get_alt() && tool == TOOL_SELECT) || (b->get_button_index() == BUTTON_LEFT && tool == TOOL_LIST_SELECT))) { // Popup the selection menu list - Point2 click = transform.xform(b->get_position()); + Point2 click = transform.affine_inverse().xform(b->get_position()); Node *scene = editor->get_edited_scene(); @@ -2399,26 +2399,34 @@ void CanvasItemEditor::_draw_straight_line(Point2 p_from, Point2 p_to, Color p_c } void CanvasItemEditor::_draw_axis() { - RID ci = viewport->get_canvas_item(); - Color x_axis_color(1.0, 0.4, 0.4, 0.6); - Color y_axis_color(0.4, 1.0, 0.4, 0.6); - Color area_axis_color(0.4, 0.4, 1.0, 0.4); + if (show_origin) { - _draw_straight_line(Point2(), Point2(1, 0), x_axis_color); - _draw_straight_line(Point2(), Point2(0, 1), y_axis_color); + Color x_axis_color(1.0, 0.4, 0.4, 0.6); + Color y_axis_color(0.4, 1.0, 0.4, 0.6); - Size2 screen_size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height")); + _draw_straight_line(Point2(), Point2(1, 0), x_axis_color); + _draw_straight_line(Point2(), Point2(0, 1), y_axis_color); + } - Vector2 screen_endpoints[4] = { - transform.xform(Vector2(0, 0)), - transform.xform(Vector2(screen_size.width, 0)), - transform.xform(Vector2(screen_size.width, screen_size.height)), - transform.xform(Vector2(0, screen_size.height)) - }; + if (show_viewport) { + + RID ci = viewport->get_canvas_item(); - for (int i = 0; i < 4; i++) { - VisualServer::get_singleton()->canvas_item_add_line(ci, screen_endpoints[i], screen_endpoints[(i + 1) % 4], area_axis_color); + Color area_axis_color(0.4, 0.4, 1.0, 0.4); + + Size2 screen_size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height")); + + Vector2 screen_endpoints[4] = { + transform.xform(Vector2(0, 0)), + transform.xform(Vector2(screen_size.width, 0)), + transform.xform(Vector2(screen_size.width, screen_size.height)), + transform.xform(Vector2(0, screen_size.height)) + }; + + for (int i = 0; i < 4; i++) { + VisualServer::get_singleton()->canvas_item_add_line(ci, screen_endpoints[i], screen_endpoints[(i + 1) % 4], area_axis_color); + } } } @@ -3002,6 +3010,18 @@ void CanvasItemEditor::_popup_callback(int p_op) { view_menu->get_popup()->set_item_checked(idx, show_grid); viewport->update(); } break; + case SHOW_ORIGIN: { + show_origin = !show_origin; + int idx = view_menu->get_popup()->get_item_index(SHOW_ORIGIN); + view_menu->get_popup()->set_item_checked(idx, show_origin); + viewport->update(); + } break; + case SHOW_VIEWPORT: { + show_viewport = !show_viewport; + int idx = view_menu->get_popup()->get_item_index(SHOW_VIEWPORT); + view_menu->get_popup()->set_item_checked(idx, show_viewport); + viewport->update(); + } break; case SNAP_USE_NODE_PARENT: { snap_node_parent = !snap_node_parent; int idx = smartsnap_config_popup->get_item_index(SNAP_USE_NODE_PARENT); @@ -3589,6 +3609,8 @@ Dictionary CanvasItemEditor::get_state() const { state["snap_grid"] = snap_grid; state["snap_guides"] = snap_guides; state["show_grid"] = show_grid; + state["show_origin"] = show_origin; + state["show_viewport"] = show_viewport; state["show_rulers"] = show_rulers; state["show_guides"] = show_guides; state["show_helpers"] = show_helpers; @@ -3682,6 +3704,18 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { view_menu->get_popup()->set_item_checked(idx, show_grid); } + if (state.has("show_origin")) { + show_origin = state["show_origin"]; + int idx = view_menu->get_popup()->get_item_index(SHOW_ORIGIN); + view_menu->get_popup()->set_item_checked(idx, show_origin); + } + + if (state.has("show_viewport")) { + show_viewport = state["show_viewport"]; + int idx = view_menu->get_popup()->get_item_index(SHOW_VIEWPORT); + view_menu->get_popup()->set_item_checked(idx, show_viewport); + } + if (state.has("show_rulers")) { show_rulers = state["show_rulers"]; int idx = view_menu->get_popup()->get_item_index(SHOW_RULERS); @@ -3955,6 +3989,8 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_helpers", TTR("Show Helpers"), KEY_H), SHOW_HELPERS); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_rulers", TTR("Show Rulers"), KEY_R), SHOW_RULERS); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_guides", TTR("Show Guides"), KEY_Y), SHOW_GUIDES); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_origin", TTR("Show Origin")), SHOW_ORIGIN); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_viewport", TTR("Show Viewport")), SHOW_VIEWPORT); p->add_separator(); p->add_shortcut(ED_SHORTCUT("canvas_item_editor/center_selection", TTR("Center Selection"), KEY_F), VIEW_CENTER_TO_SELECTION); p->add_shortcut(ED_SHORTCUT("canvas_item_editor/frame_selection", TTR("Frame Selection"), KEY_MASK_SHIFT | KEY_F), VIEW_FRAME_TO_SELECTION); @@ -4040,6 +4076,8 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { key_scale = false; show_grid = false; + show_origin = true; + show_viewport = true; show_helpers = false; show_rulers = true; show_guides = true; diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index adf203cf3d..a6e2ef800a 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -100,6 +100,8 @@ class CanvasItemEditor : public VBoxContainer { SHOW_HELPERS, SHOW_RULERS, SHOW_GUIDES, + SHOW_ORIGIN, + SHOW_VIEWPORT, LOCK_SELECTED, UNLOCK_SELECTED, GROUP_SELECTED, @@ -215,6 +217,8 @@ class CanvasItemEditor : public VBoxContainer { bool show_grid; bool show_rulers; bool show_guides; + bool show_origin; + bool show_viewport; bool show_helpers; float zoom; diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 47d730cdf1..8542296bde 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -40,7 +40,7 @@ #include "scene/resources/material.h" #include "scene/resources/mesh.h" -static void post_process_preview(Ref<Image> p_image) { +void post_process_preview(Ref<Image> p_image) { if (p_image->get_format() != Image::FORMAT_RGBA8) p_image->convert(Image::FORMAT_RGBA8); diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h index 2e12515e30..35b5c3a5f0 100644 --- a/editor/plugins/editor_preview_plugins.h +++ b/editor/plugins/editor_preview_plugins.h @@ -33,6 +33,8 @@ #include "editor/editor_resource_preview.h" +void post_process_preview(Ref<Image> p_image); + class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator { GDCLASS(EditorTexturePreviewPlugin, EditorResourcePreviewGenerator) public: diff --git a/editor/plugins/item_list_editor_plugin.cpp b/editor/plugins/item_list_editor_plugin.cpp index fd5a1f185f..5020f5b851 100644 --- a/editor/plugins/item_list_editor_plugin.cpp +++ b/editor/plugins/item_list_editor_plugin.cpp @@ -247,7 +247,7 @@ void ItemListEditor::_node_removed(Node *p_node) { void ItemListEditor::_notification(int p_notification) { - if (p_notification == NOTIFICATION_ENTER_TREE) { + if (p_notification == NOTIFICATION_ENTER_TREE || p_notification == NOTIFICATION_THEME_CHANGED) { add_button->set_icon(get_icon("Add", "EditorIcons")); del_button->set_icon(get_icon("Remove", "EditorIcons")); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 261bece8f7..09388870f1 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -2577,8 +2577,8 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { waiting_update_names = false; pending_auto_reload = false; auto_reload_running_scripts = false; - members_overview_enabled = true; - help_overview_enabled = true; + members_overview_enabled = EditorSettings::get_singleton()->get("text_editor/open_scripts/show_members_overview"); + help_overview_enabled = EditorSettings::get_singleton()->get("text_editor/help/show_help_index"); editor = p_editor; VBoxContainer *main_container = memnew(VBoxContainer); @@ -2820,9 +2820,9 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { //debugger_gui->hide(); edit_pass = 0; - trim_trailing_whitespace_on_save = false; - convert_indent_on_save = false; - use_space_indentation = false; + trim_trailing_whitespace_on_save = EditorSettings::get_singleton()->get("text_editor/files/trim_trailing_whitespace_on_save"); + convert_indent_on_save = EditorSettings::get_singleton()->get("text_editor/indent/convert_indent_on_save"); + use_space_indentation = EditorSettings::get_singleton()->get("text_editor/indent/type"); ScriptServer::edit_request_func = _open_script_request; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index aeba0ff930..c8ea2f79fe 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -349,7 +349,12 @@ void ScriptTextEditor::_convert_case(CaseStyle p_case) { int end_col = te->get_selection_to_column(); for (int i = begin; i <= end; i++) { - String new_line = te->get_line(i); + int len = te->get_line(i).length(); + if (i == end) + len -= len - end_col; + if (i == begin) + len -= begin_col; + String new_line = te->get_line(i).substr(i == begin ? begin_col : 0, len); switch (p_case) { case UPPER: { @@ -364,10 +369,10 @@ void ScriptTextEditor::_convert_case(CaseStyle p_case) { } if (i == begin) { - new_line = te->get_line(i).left(begin_col) + new_line.right(begin_col); + new_line = te->get_line(i).left(begin_col) + new_line; } if (i == end) { - new_line = new_line.left(end_col) + te->get_line(i).right(end_col); + new_line = new_line + te->get_line(i).right(end_col); } te->set_line(i, new_line); } diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 6cb4ca1e86..4367fe8976 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -57,8 +57,9 @@ void TextureRegionEditor::_region_draw() { base_tex = obj_styleBox->get_texture(); else if (atlas_tex.is_valid()) base_tex = atlas_tex->get_atlas(); - else if (tile_set.is_valid() && selected_tile != -1) + else if (tile_set.is_valid() && selected_tile != -1 && tile_set->has_tile(selected_tile)) base_tex = tile_set->tile_get_texture(selected_tile); + if (base_tex.is_null()) return; @@ -600,6 +601,7 @@ void TextureRegionEditor::apply_rect(const Rect2 &rect) { void TextureRegionEditor::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_READY: { zoom_out->set_icon(get_icon("ZoomLess", "EditorIcons")); zoom_reset->set_icon(get_icon("ZoomReset", "EditorIcons")); @@ -688,10 +690,11 @@ void TextureRegionEditor::_edit_region() { texture = obj_styleBox->get_texture(); else if (atlas_tex.is_valid()) texture = atlas_tex->get_atlas(); - else if (tile_set.is_valid() && selected_tile != -1) + else if (tile_set.is_valid() && selected_tile != -1 && tile_set->has_tile(selected_tile)) texture = tile_set->tile_get_texture(selected_tile); if (texture.is_null()) { + edit_draw->update(); return; } @@ -780,6 +783,7 @@ TextureRegionEditor::TextureRegionEditor(EditorNode *p_editor) { tile_set = Ref<TileSet>(NULL); editor = p_editor; undo_redo = editor->get_undo_redo(); + selected_tile = -1; snap_step = Vector2(10, 10); snap_separation = Vector2(0, 0); diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index c97e949403..c5c7272ed2 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -48,6 +48,20 @@ void TileMapEditor::_notification(int p_what) { } break; + case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { + + bool new_show_tile_info = EditorSettings::get_singleton()->get("editors/tile_map/show_tile_info_on_hover"); + if (new_show_tile_info != show_tile_info) { + show_tile_info = new_show_tile_info; + tile_info->set_visible(show_tile_info); + } + + if (is_visible_in_tree()) { + _update_palette(); + } + + } // fallthrough + case NOTIFICATION_ENTER_TREE: { transp->set_icon(get_icon("Transpose", "EditorIcons")); @@ -60,19 +74,13 @@ void TileMapEditor::_notification(int p_what) { search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); - } break; - - case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - - bool new_show_tile_info = EditorSettings::get_singleton()->get("editors/tile_map/show_tile_info_on_hover"); - if (new_show_tile_info != show_tile_info) { - show_tile_info = new_show_tile_info; - tile_info->set_visible(show_tile_info); - } + PopupMenu *p = options->get_popup(); + p->set_item_icon(p->get_item_index(OPTION_PAINTING), get_icon("Edit", "EditorIcons")); + p->set_item_icon(p->get_item_index(OPTION_PICK_TILE), get_icon("ColorPick", "EditorIcons")); + p->set_item_icon(p->get_item_index(OPTION_SELECT), get_icon("ToolSelect", "EditorIcons")); + p->set_item_icon(p->get_item_index(OPTION_DUPLICATE), get_icon("Duplicate", "EditorIcons")); + p->set_item_icon(p->get_item_index(OPTION_ERASE_SELECTION), get_icon("Remove", "EditorIcons")); - if (is_visible_in_tree()) { - _update_palette(); - } } break; } } @@ -139,6 +147,15 @@ void TileMapEditor::_menu_option(int p_option) { canvas_item_editor->update(); } break; + case OPTION_FIX_INVALID: { + + undo_redo->create_action(TTR("Fix Invalid Tiles")); + undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data")); + node->fix_invalid_tiles(); + undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data")); + undo_redo->commit_action(); + + } break; } } @@ -1511,8 +1528,8 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { bucket_cache_tile = -1; bucket_cache_visited = 0; - ED_SHORTCUT("tile_map_editor/erase_selection", TTR("Erase selection"), KEY_DELETE); - ED_SHORTCUT("tile_map_editor/find_tile", TTR("Find tile"), KEY_MASK_CMD + KEY_F); + ED_SHORTCUT("tile_map_editor/erase_selection", TTR("Erase Selection"), KEY_DELETE); + ED_SHORTCUT("tile_map_editor/find_tile", TTR("Find Tile"), KEY_MASK_CMD + KEY_F); ED_SHORTCUT("tile_map_editor/transpose", TTR("Transpose"), KEY_T); ED_SHORTCUT("tile_map_editor/mirror_x", TTR("Mirror X"), KEY_A); ED_SHORTCUT("tile_map_editor/mirror_y", TTR("Mirror Y"), KEY_S); @@ -1575,6 +1592,8 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { p->add_shortcut(ED_SHORTCUT("tile_map_editor/select", TTR("Select"), KEY_MASK_CMD + KEY_B), OPTION_SELECT); p->add_shortcut(ED_SHORTCUT("tile_map_editor/duplicate_selection", TTR("Duplicate Selection"), KEY_MASK_CMD + KEY_D), OPTION_DUPLICATE); p->add_shortcut(ED_GET_SHORTCUT("tile_map_editor/erase_selection"), OPTION_ERASE_SELECTION); + p->add_separator(); + p->add_item(TTR("Fix Invalid Tiles"), OPTION_FIX_INVALID); p->connect("id_pressed", this, "_menu_option"); diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h index 0a937e200e..2d582d030b 100644 --- a/editor/plugins/tile_map_editor_plugin.h +++ b/editor/plugins/tile_map_editor_plugin.h @@ -71,6 +71,7 @@ class TileMapEditor : public VBoxContainer { OPTION_DUPLICATE, OPTION_ERASE_SELECTION, OPTION_PAINTING, + OPTION_FIX_INVALID, }; TileMap *node; diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index 686ad566fd..2311439728 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -242,6 +242,7 @@ void TileSetEditor::_bind_methods() { ClassDB::bind_method("_name_dialog_confirm", &TileSetEditor::_name_dialog_confirm); ClassDB::bind_method("_on_tile_list_selected", &TileSetEditor::_on_tile_list_selected); ClassDB::bind_method("_on_edit_mode_changed", &TileSetEditor::_on_edit_mode_changed); + ClassDB::bind_method("_on_workspace_overlay_draw", &TileSetEditor::_on_workspace_overlay_draw); ClassDB::bind_method("_on_workspace_draw", &TileSetEditor::_on_workspace_draw); ClassDB::bind_method("_on_workspace_input", &TileSetEditor::_on_workspace_input); ClassDB::bind_method("_on_tool_clicked", &TileSetEditor::_on_tool_clicked); @@ -256,7 +257,7 @@ void TileSetEditor::_bind_methods() { } void TileSetEditor::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) { + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { tools[TOOL_SELECT]->set_icon(get_icon("ToolSelect", "EditorIcons")); tools[BITMASK_COPY]->set_icon(get_icon("Duplicate", "EditorIcons")); tools[BITMASK_PASTE]->set_icon(get_icon("Override", "EditorIcons")); @@ -519,10 +520,15 @@ void TileSetEditor::initialize_bottom_editor() { workspace_container = memnew(Control); scroll->add_child(workspace_container); + workspace_overlay = memnew(Control); + workspace_overlay->connect("draw", this, "_on_workspace_overlay_draw"); + workspace_container->add_child(workspace_overlay); + workspace = memnew(Control); workspace->connect("draw", this, "_on_workspace_draw"); workspace->connect("gui_input", this, "_on_workspace_input"); - workspace_container->add_child(workspace); + workspace->set_draw_behind_parent(true); + workspace_overlay->add_child(workspace); preview = memnew(Sprite); workspace->add_child(preview); @@ -558,6 +564,8 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { add_child(err_dialog); err_dialog->set_title(TTR("Error")); + draw_handles = false; + initialize_bottom_editor(); } @@ -739,9 +747,23 @@ void TileSetEditor::_on_workspace_draw() { } } } + workspace_overlay->update(); +} + +void TileSetEditor::_on_workspace_overlay_draw() { + + int t_id = get_current_tile(); + if (t_id < 0 || !draw_handles) + return; + + Ref<Texture> handle = get_icon("EditorHandle", "EditorIcons"); + + for (int i = 0; i < current_shape.size(); i++) { + workspace_overlay->draw_texture(handle, current_shape[i] * workspace->get_scale().x - handle->get_size() * 0.5); + } } -#define MIN_DISTANCE_SQUARED 10 +#define MIN_DISTANCE_SQUARED 6 void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { if (get_current_tile() >= 0 && !tileset.is_null()) { @@ -1162,15 +1184,18 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { scale /= 2; workspace->set_scale(Vector2(scale, scale)); workspace_container->set_custom_minimum_size(preview->get_region_rect().size * scale); + workspace_overlay->set_custom_minimum_size(preview->get_region_rect().size * scale); } } else if (p_tool == ZOOM_1) { workspace->set_scale(Vector2(1, 1)); workspace_container->set_custom_minimum_size(preview->get_region_rect().size); + workspace_overlay->set_custom_minimum_size(preview->get_region_rect().size); } else if (p_tool == ZOOM_IN) { float scale = workspace->get_scale().x; scale *= 2; workspace->set_scale(Vector2(scale, scale)); workspace_container->set_custom_minimum_size(preview->get_region_rect().size * scale); + workspace_overlay->set_custom_minimum_size(preview->get_region_rect().size * scale); } else if (p_tool == TOOL_SELECT) { if (creating_shape) { //Cancel Creation @@ -1292,6 +1317,8 @@ void TileSetEditor::draw_polygon_shapes() { if (t_id < 0) return; + draw_handles = false; + switch (edit_mode) { case EDITMODE_COLLISION: { Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(t_id); @@ -1339,9 +1366,7 @@ void TileSetEditor::draw_polygon_shapes() { } if (shape == edited_collision_shape) { - for (int j = 0; j < current_shape.size(); j++) { - workspace->draw_circle(current_shape[j], 8 / workspace->get_scale().x, Color(1, 0, 0, 0.7f)); - } + draw_handles = true; } } } @@ -1367,9 +1392,7 @@ void TileSetEditor::draw_polygon_shapes() { } workspace->draw_line(shape->get_polygon()[shape->get_polygon().size() - 1], shape->get_polygon()[0], c_border, 1, true); if (shape == edited_occlusion_shape) { - for (int j = 0; j < current_shape.size(); j++) { - workspace->draw_circle(current_shape[j], 8 / workspace->get_scale().x, Color(1, 0, 0)); - } + draw_handles = true; } } } else { @@ -1412,9 +1435,7 @@ void TileSetEditor::draw_polygon_shapes() { } workspace->draw_line(shape->get_polygon()[shape->get_polygon().size() - 1] + anchor, shape->get_polygon()[0] + anchor, c_border, 1, true); if (shape == edited_occlusion_shape) { - for (int j = 0; j < current_shape.size(); j++) { - workspace->draw_circle(current_shape[j], 8 / workspace->get_scale().x, Color(1, 0, 0)); - } + draw_handles = true; } } } @@ -1445,9 +1466,7 @@ void TileSetEditor::draw_polygon_shapes() { workspace->draw_line(vertices[shape->get_polygon(0)[j]], vertices[shape->get_polygon(0)[j + 1]], c_border, 1, true); } if (shape == edited_navigation_shape) { - for (int j = 0; j < current_shape.size(); j++) { - workspace->draw_circle(current_shape[j], 8 / workspace->get_scale().x, Color(1, 0, 0)); - } + draw_handles = true; } } } @@ -1494,9 +1513,7 @@ void TileSetEditor::draw_polygon_shapes() { workspace->draw_line(vertices[shape->get_polygon(0)[j]] + anchor, vertices[shape->get_polygon(0)[j + 1]] + anchor, c_border, 1, true); } if (shape == edited_navigation_shape) { - for (int j = 0; j < current_shape.size(); j++) { - workspace->draw_circle(current_shape[j], 8 / workspace->get_scale().x, Color(1, 0, 0)); - } + draw_handles = true; } } } @@ -1809,6 +1826,8 @@ bool TileSetEditorHelper::_get(const StringName &p_name, Variant &r_ret) const { if (selected_tile < 0 || tileset.is_null()) return false; + if (!tileset->has_tile(selected_tile)) + return false; String name = p_name.operator String(); bool v = false; @@ -1833,6 +1852,7 @@ void TileSetEditorHelper::_get_property_list(List<PropertyInfo> *p_list) const { TileSetEditorHelper::TileSetEditorHelper(TileSetEditor *p_tileset_editor) { tileset_editor = p_tileset_editor; + selected_tile = -1; } void TileSetEditorPlugin::edit(Object *p_node) { diff --git a/editor/plugins/tile_set_editor_plugin.h b/editor/plugins/tile_set_editor_plugin.h index f57ec9a117..4894d641a3 100644 --- a/editor/plugins/tile_set_editor_plugin.h +++ b/editor/plugins/tile_set_editor_plugin.h @@ -91,6 +91,8 @@ class TileSetEditor : public Control { Sprite *preview; ScrollContainer *scroll; Control *workspace_container; + bool draw_handles; + Control *workspace_overlay; Control *workspace; Button *tool_editmode[EDITMODE_MAX]; HBoxContainer *tool_containers[TOOLBAR_MAX]; @@ -160,6 +162,7 @@ public: private: void _on_tile_list_selected(int p_index); void _on_edit_mode_changed(int p_edit_mode); + void _on_workspace_overlay_draw(); void _on_workspace_draw(); void _on_workspace_input(const Ref<InputEvent> &p_ie); void _on_tool_clicked(int p_tool); diff --git a/editor/project_export.cpp b/editor/project_export.cpp index d15a37a19c..4dbd9d500e 100644 --- a/editor/project_export.cpp +++ b/editor/project_export.cpp @@ -56,6 +56,12 @@ void ProjectExportDialog::_notification(int p_what) { case NOTIFICATION_POPUP_HIDE: { EditorSettings::get_singleton()->set("interface/dialogs/export_bounds", get_rect()); } break; + case NOTIFICATION_THEME_CHANGED: { + delete_preset->set_icon(get_icon("Remove", "EditorIcons")); + Control *panel = custom_feature_display->get_parent_control(); + if (panel) + panel->add_style_override("panel", get_stylebox("bg", "Tree")); + } break; } } diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 9625bc19c0..8080a04a67 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -110,6 +110,14 @@ void ProjectSettingsEditor::_notification(int p_what) { EditorSettings::get_singleton()->set("interface/dialogs/project_settings_bounds", get_rect()); } break; case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { + + search_button->set_icon(get_icon("Search", "EditorIcons")); + clear_button->set_icon(get_icon("Close", "EditorIcons")); + action_add_error->add_color_override("font_color", get_color("error_color", "Editor")); + popup_add->set_item_icon(popup_add->get_item_index(INPUT_KEY), get_icon("Keyboard", "EditorIcons")); + popup_add->set_item_icon(popup_add->get_item_index(INPUT_JOY_BUTTON), get_icon("JoyButton", "EditorIcons")); + popup_add->set_item_icon(popup_add->get_item_index(INPUT_JOY_MOTION), get_icon("JoyAxis", "EditorIcons")); + popup_add->set_item_icon(popup_add->get_item_index(INPUT_MOUSE_BUTTON), get_icon("Mouse", "EditorIcons")); _update_actions(); } break; } @@ -137,12 +145,12 @@ void ProjectSettingsEditor::_action_edited() { if (new_name == old_name) return; - if (new_name.find("/") != -1 || new_name.find(":") != -1 || new_name == "") { + if (new_name.find("/") != -1 || new_name.find(":") != -1 || new_name.find("\"") != -1 || new_name == "") { ti->set_text(0, old_name); add_at = "input/" + old_name; - message->set_text(TTR("Invalid action (anything goes but '/' or ':').")); + message->set_text(TTR("Invalid action (anything goes but '/', ':' or '\"').")); message->popup_centered(Size2(300, 100) * EDSCALE); return; } @@ -830,9 +838,9 @@ void ProjectSettingsEditor::_action_check(String p_action) { action_add->set_disabled(true); } else { - if (p_action.find("/") != -1 || p_action.find(":") != -1) { + if (p_action.find("/") != -1 || p_action.find(":") != -1 || p_action.find("\"") != -1) { - action_add_error->set_text(TTR("Can't contain '/' or ':'")); + action_add_error->set_text(TTR("Can't contain '/', ':' or '\"'")); action_add_error->show(); action_add->set_disabled(true); return; diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 9f0f62592b..002d702bac 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -1411,6 +1411,7 @@ void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node) { Node *n = p_node; Node *newnode = p_by_node; + Node *default_oldnode = Object::cast_to<Node>(ClassDB::instance(n->get_class())); List<PropertyInfo> pinfo; n->get_property_list(&pinfo); @@ -1419,8 +1420,11 @@ void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node) { continue; if (E->get().name == "__meta__") continue; - newnode->set(E->get().name, n->get(E->get().name)); + if (default_oldnode->get(E->get().name) != n->get(E->get().name)) { + newnode->set(E->get().name, n->get(E->get().name)); + } } + memdelete(default_oldnode); editor->push_item(NULL); diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index 8f3113c816..e38347a653 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -550,6 +550,10 @@ void SceneTreeEditor::_notification(int p_what) { get_tree()->disconnect("node_configuration_warning_changed", this, "_warning_changed"); EditorSettings::get_singleton()->disconnect("settings_changed", this, "_editor_settings_changed"); } + if (p_what == NOTIFICATION_THEME_CHANGED) { + + _update_tree(); + } } TreeItem *SceneTreeEditor::_find(TreeItem *p_node, const NodePath &p_path) { diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index b893b098ac..57a003060e 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -41,10 +41,12 @@ void ScriptCreateDialog::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_ENTER_TREE: { path_button->set_icon(get_icon("Folder", "EditorIcons")); parent_browse_button->set_icon(get_icon("Folder", "EditorIcons")); - } + status_panel->add_style_override("panel", get_stylebox("bg", "Tree")); + } break; } } @@ -434,6 +436,13 @@ void ScriptCreateDialog::_path_changed(const String &p_path) { return; } + String path_error = ScriptServer::get_language(language_menu->get_selected())->validate_path(p); + if (path_error != "") { + _msg_path_valid(false, path_error); + _update_dialog(); + return; + } + /* All checks passed */ is_path_valid = true; @@ -609,10 +618,10 @@ ScriptCreateDialog::ScriptCreateDialog() { hb->add_child(path_error_label); vb->add_child(hb); - PanelContainer *pc = memnew(PanelContainer); - pc->set_h_size_flags(Control::SIZE_FILL); - pc->add_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_stylebox("bg", "Tree")); - pc->add_child(vb); + status_panel = memnew(PanelContainer); + status_panel->set_h_size_flags(Control::SIZE_FILL); + status_panel->add_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_stylebox("bg", "Tree")); + status_panel->add_child(vb); /* Margins */ @@ -631,7 +640,7 @@ ScriptCreateDialog::ScriptCreateDialog() { vb->add_child(empty_h->duplicate()); vb->add_child(gc); vb->add_child(empty_h->duplicate()); - vb->add_child(pc); + vb->add_child(status_panel); vb->add_child(empty_h->duplicate()); hb = memnew(HBoxContainer); hb->add_child(empty_v->duplicate()); diff --git a/editor/script_create_dialog.h b/editor/script_create_dialog.h index 99e5bc9e6a..1ad4a1b7a1 100644 --- a/editor/script_create_dialog.h +++ b/editor/script_create_dialog.h @@ -46,6 +46,7 @@ class ScriptCreateDialog : public ConfirmationDialog { LineEdit *class_name; Label *error_label; Label *path_error_label; + PanelContainer *status_panel; LineEdit *parent_name; Button *parent_browse_button; OptionButton *language_menu; diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index 7fa3ed34f0..e9529eb1c0 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/script_editor_debugger.cpp @@ -1160,6 +1160,16 @@ void ScriptEditorDebugger::_notification(int p_what) { inspect_scene_tree->add_color_override("relationship_line_color", rl_color); } else inspect_scene_tree->add_constant_override("draw_relationship_lines", 0); + + copy->set_icon(get_icon("ActionCopy", "EditorIcons")); + step->set_icon(get_icon("DebugStep", "EditorIcons")); + next->set_icon(get_icon("DebugNext", "EditorIcons")); + back->set_icon(get_icon("Back", "EditorIcons")); + forward->set_icon(get_icon("Forward", "EditorIcons")); + dobreak->set_icon(get_icon("Pause", "EditorIcons")); + docontinue->set_icon(get_icon("DebugContinue", "EditorIcons")); + vmem_refresh->set_icon(get_icon("Reload", "EditorIcons")); + } break; } } diff --git a/editor/translations/extract.py b/editor/translations/extract.py index bbc157788d..4b3f416343 100755 --- a/editor/translations/extract.py +++ b/editor/translations/extract.py @@ -52,11 +52,7 @@ msgstr "" "Content-Transfer-Encoding: 8-bit\\n" """ -print("Updating the editor.pot template...") - -for fname in matches: - - f = open(fname, "rb") +def process_file(f, fname): l = f.readline() lc = 1 @@ -100,12 +96,14 @@ for fname in matches: l = f.readline() lc += 1 - f.close() +print("Updating the editor.pot template...") +for fname in matches: + with open(fname, "rb") as f: + process_file(f, fname) -f = open("editor.pot", "wb") -f.write(main_po) -f.close() +with open("editor.pot", "wb") as f: + f.write(main_po) if (os.name == "posix"): print("Wrapping template at 79 characters for compatibility with Weblate.") diff --git a/main/SCsub b/main/SCsub index 8b4f878c85..ab4016190d 100644 --- a/main/SCsub +++ b/main/SCsub @@ -8,60 +8,59 @@ def make_splash(target, source, env): src = source[0].srcnode().abspath dst = target[0].srcnode().abspath - f = open(src, "rb") - g = open(dst, "w") - buf = f.read() + with open(src, "rb") as f: + buf = f.read() - g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - g.write("#ifndef BOOT_SPLASH_H\n") - g.write("#define BOOT_SPLASH_H\n") - g.write('static const Color boot_splash_bg_color = Color::html("#232323");\n') - g.write("static const unsigned char boot_splash_png[] = {\n") - for i in range(len(buf)): - g.write(byte_to_str(buf[i]) + ",\n") - g.write("};\n") - g.write("#endif") + with open(dst, "w") as g: + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef BOOT_SPLASH_H\n") + g.write("#define BOOT_SPLASH_H\n") + g.write('static const Color boot_splash_bg_color = Color::html("#232323");\n') + g.write("static const unsigned char boot_splash_png[] = {\n") + for i in range(len(buf)): + g.write(byte_to_str(buf[i]) + ",\n") + g.write("};\n") + g.write("#endif") def make_splash_editor(target, source, env): src = source[0].srcnode().abspath dst = target[0].srcnode().abspath - f = open(src, "rb") - g = open(dst, "w") - buf = f.read() - - g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - g.write("#ifndef BOOT_SPLASH_EDITOR_H\n") - g.write("#define BOOT_SPLASH_EDITOR_H\n") - g.write('static const Color boot_splash_editor_bg_color = Color::html("#232323");\n') - g.write("static const unsigned char boot_splash_editor_png[] = {\n") - for i in range(len(buf)): - g.write(byte_to_str(buf[i]) + ",\n") - g.write("};\n") - g.write("#endif") + with open(src, "rb") as f: + buf = f.read() + with open(dst, "w") as g: + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef BOOT_SPLASH_EDITOR_H\n") + g.write("#define BOOT_SPLASH_EDITOR_H\n") + g.write('static const Color boot_splash_editor_bg_color = Color::html("#232323");\n') + g.write("static const unsigned char boot_splash_editor_png[] = {\n") + for i in range(len(buf)): + g.write(byte_to_str(buf[i]) + ",\n") + g.write("};\n") + g.write("#endif") def make_app_icon(target, source, env): src = source[0].srcnode().abspath dst = target[0].srcnode().abspath - f = open(src, "rb") - g = open(dst, "w") - - buf = f.read() - g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - g.write("#ifndef APP_ICON_H\n") - g.write("#define APP_ICON_H\n") - g.write("static const unsigned char app_icon_png[] = {\n") - for i in range(len(buf)): - g.write(byte_to_str(buf[i]) + ",\n") - g.write("};\n") - g.write("#endif") + with open(src, "rb") as f: + buf = f.read() + + with open(dst, "w") as g: + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef APP_ICON_H\n") + g.write("#define APP_ICON_H\n") + g.write("static const unsigned char app_icon_png[] = {\n") + for i in range(len(buf)): + g.write(byte_to_str(buf[i]) + ",\n") + g.write("};\n") + g.write("#endif") def make_default_controller_mappings(target, source, env): dst = target[0].srcnode().abspath diff --git a/main/main.cpp b/main/main.cpp index 82b79cd9a2..f0b8fea206 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -689,6 +689,19 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph I = N; } + if (globals->setup(game_path, main_pack, upwards) == OK) { + found_project = true; + } else { + +#ifdef TOOLS_ENABLED + editor = false; +#else + OS::get_singleton()->print("Error: Could not load game path '%s'.\n", game_path.ascii().get_data()); + + goto error; +#endif + } + GLOBAL_DEF("memory/limits/multithreaded_server/rid_pool_prealloc", 60); GLOBAL_DEF("network/limits/debugger_stdout/max_chars_per_second", 2048); GLOBAL_DEF("network/limits/debugger_stdout/max_messages_per_frame", 10); @@ -761,19 +774,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph #endif - if (globals->setup(game_path, main_pack, upwards) == OK) { - found_project = true; - } else { - -#ifdef TOOLS_ENABLED - editor = false; -#else - OS::get_singleton()->print("Error: Could not load game path '%s'.\n", game_path.ascii().get_data()); - - goto error; -#endif - } - GLOBAL_DEF("logging/file_logging/enable_file_logging", false); GLOBAL_DEF("logging/file_logging/log_path", "user://logs/log.txt"); GLOBAL_DEF("logging/file_logging/max_log_files", 10); diff --git a/methods.py b/methods.py index 42679610af..6f03f79be3 100644 --- a/methods.py +++ b/methods.py @@ -597,6 +597,9 @@ def parse_cg_file(fname, uniforms, sizes, conditionals): line = fs.readline() + fs.close() + + import glob @@ -644,8 +647,8 @@ void unregister_module_types() { } """ - f = open("modules/register_module_types.gen.cpp", "w") - f.write(modules_cpp) + with open("modules/register_module_types.gen.cpp", "w") as f: + f.write(modules_cpp) return module_list @@ -744,18 +747,18 @@ def android_add_default_config(self, config): def android_add_to_manifest(self, file): base_path = self.Dir(".").abspath + "/modules/" + self.current_module + "/" + file - f = open(base_path, "r") - self.android_manifest_chunk += f.read() + with open(base_path, "r") as f: + self.android_manifest_chunk += f.read() def android_add_to_permissions(self, file): base_path = self.Dir(".").abspath + "/modules/" + self.current_module + "/" + file - f = open(base_path, "r") - self.android_permission_chunk += f.read() + with open(base_path, "r") as f: + self.android_permission_chunk += f.read() def android_add_to_attributes(self, file): base_path = self.Dir(".").abspath + "/modules/" + self.current_module + "/" + file - f = open(base_path, "r") - self.android_appattributes_chunk += f.read() + with open(base_path, "r") as f: + self.android_appattributes_chunk += f.read() def disable_module(self): self.disabled_modules.append(self.current_module) @@ -886,9 +889,11 @@ def save_active_platforms(apnames, ap): str += "};\n" + pngf.close() + wf = x + "/" + name + ".gen.h" - pngw = open(wf, "w") - pngw.write(str) + with open(wf, "w") as pngw: + pngw.write(str) def no_verbose(sys, env): @@ -1048,8 +1053,8 @@ def generate_cpp_hint_file(filename): pass else: try: - fd = open(filename, "w") - fd.write("#define GDCLASS(m_class, m_inherits)\n") + with open(filename, "w") as fd: + fd.write("#define GDCLASS(m_class, m_inherits)\n") except IOError: print("Could not write cpp.hint file.") diff --git a/misc/dist/html/default.html b/misc/dist/html/default.html index a1a4e89d02..4e3515a7b6 100644 --- a/misc/dist/html/default.html +++ b/misc/dist/html/default.html @@ -244,9 +244,6 @@ $GODOT_HEAD_INCLUDE var statusMode = 'hidden'; var indeterminiateStatusAnimationId = 0; - setStatusMode('indeterminate'); - engine.setCanvas(canvas); - function setStatusMode(mode) { if (statusMode === mode || !initializing) @@ -367,18 +364,27 @@ $GODOT_HEAD_INCLUDE }); } - engine.startGame(BASENAME + '.pck').then(() => { - setStatusMode('hidden'); - initializing = false; - }, err => { + function displayFailureNotice(err) { + var msg = err.message || err; if (DEBUG_ENABLED) { - printError(err.message); - console.warn(err); + printError(msg); } - setStatusNotice(err.message); + console.error(msg); + setStatusNotice(msg); setStatusMode('notice'); initializing = false; - }); + }; + + if (!Engine.isWebGLAvailable()) { + displayFailureNotice("WebGL not available"); + } else { + setStatusMode('indeterminate'); + engine.setCanvas(canvas); + engine.startGame(BASENAME + '.pck').then(() => { + setStatusMode('hidden'); + initializing = false; + }, displayFailureNotice); + } })(); //]]></script> </body> diff --git a/misc/dist/linux/godot.desktop b/misc/dist/linux/godot.desktop index 545c491256..974352b117 100644 --- a/misc/dist/linux/godot.desktop +++ b/misc/dist/linux/godot.desktop @@ -2,7 +2,7 @@ Name=Godot Engine GenericName=Libre game engine Comment=Multi-platform 2D and 3D game engine with a feature rich editor -Exec=godot -pm +Exec=godot -p Icon=godot Terminal=false Type=Application diff --git a/misc/hooks/pre-commit-clang-format b/misc/hooks/pre-commit-clang-format index 8ef4e27748..016a43038b 100755 --- a/misc/hooks/pre-commit-clang-format +++ b/misc/hooks/pre-commit-clang-format @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # git pre-commit hook that runs a clang-format stylecheck. # Features: diff --git a/misc/scripts/fix_headers.py b/misc/scripts/fix_headers.py index 809820c20f..48b9628b1f 100644 --- a/misc/scripts/fix_headers.py +++ b/misc/scripts/fix_headers.py @@ -92,9 +92,11 @@ while (fname != ""): fileread.close() # Write - fileread = open(fname.strip(), "wb") - fileread.write(text) - fileread.close() + filewrite = open(fname.strip(), "wb") + filewrite.write(text) + filewrite.close() # Next file fname = files.readline() + +files.close()
\ No newline at end of file diff --git a/misc/scripts/make_glwrapper.py b/misc/scripts/make_glwrapper.py index 5694d2327e..15b66a950b 100644 --- a/misc/scripts/make_glwrapper.py +++ b/misc/scripts/make_glwrapper.py @@ -16,9 +16,7 @@ READ_CONSTANTS = 2 read_what = READ_TYPES -for x in (range(len(sys.argv) - 1)): - f = open(sys.argv[x + 1], "r") - +def read_file(f): while(True): line = f.readline() @@ -86,6 +84,9 @@ for x in (range(len(sys.argv) - 1)): functions.append(funcdata) print(funcdata) +for path in sys.argv[1:]: + with open(path, "r") as f: + read_file(f) # print(types) # print(constants) @@ -156,6 +157,7 @@ f.write("void glWrapperInit( GLWrapperFuncPtr (*wrapperFunc)(const char*) );\n") f.write("#ifdef __cplusplus\n}\n#endif\n") f.write("#endif\n\n") +f.close() f = open("glwrapper.c", "w") @@ -176,3 +178,4 @@ for x in functions: f.write("\n\n") f.write("}\n") f.write("\n\n") +f.close() diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp index 4a0c7499b4..6246a295ec 100644 --- a/modules/bullet/bullet_physics_server.cpp +++ b/modules/bullet/bullet_physics_server.cpp @@ -89,7 +89,9 @@ BulletPhysicsServer::BulletPhysicsServer() : active(true), active_spaces_count(0) {} -BulletPhysicsServer::~BulletPhysicsServer() {} +BulletPhysicsServer::~BulletPhysicsServer() { + bulletdelete(emptyShape); +} RID BulletPhysicsServer::shape_create(ShapeType p_shape) { ShapeBullet *shape = NULL; diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml index 5f94353840..7bee63019b 100644 --- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml +++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml @@ -32,7 +32,7 @@ <argument index="3" name="out_bandwidth" type="int" default="0"> </argument> <description> - Create client that connects to a server at address [code]ip[/code] using specified [code]port[/code]. The given IP needs to be in IPv4 or IPv6 address format, for example: [code]192.168.1.1[/code]. The [code]port[/code] is the port the server is listening on. The [code]in_bandwidth[/code] and [code]out_bandwidth[/code] parameters can be used to limit the incoming and outgoing bandwidth to the given number of bytes per second. The default of 0 means unlimited bandwidth. Note that ENet will strategically drop packets on specific sides of a connection between peers to ensure the peer's bandwidth is not overwhelmed. The bandwidth parameters also determine the window size of a connection which limits the amount of reliable packets that may be in transit at any given time. + Create client that connects to a server at address [code]ip[/code] using specified [code]port[/code]. The given IP needs to be in IPv4 or IPv6 address format, for example: [code]192.168.1.1[/code]. The [code]port[/code] is the port the server is listening on. The [code]in_bandwidth[/code] and [code]out_bandwidth[/code] parameters can be used to limit the incoming and outgoing bandwidth to the given number of bytes per second. The default of 0 means unlimited bandwidth. Note that ENet will strategically drop packets on specific sides of a connection between peers to ensure the peer's bandwidth is not overwhelmed. The bandwidth parameters also determine the window size of a connection which limits the amount of reliable packets that may be in transit at any given time. Returns [code]OK[/code] if a client was created, [code]ERR_ALREADY_IN_USE[/code] if this NetworkedMultiplayerEnet instance already has an open connection (in which case you need to call [method close_connection] first) or [code]ERR_CANT_CREATE[/code] if the client could not be created. </description> </method> <method name="create_server"> @@ -47,7 +47,7 @@ <argument index="3" name="out_bandwidth" type="int" default="0"> </argument> <description> - Create server that listens to connections via [code]port[/code]. The port needs to be an available, unused port between 0 and 65535. Note that ports below 1024 are privileged and may require elevated permissions depending on the platform. To change the interface the server listens on, use [method set_bind_ip]. The default IP is the wildcard [code]*[/code], which listens on all available interfaces. [code]max_clients[/code] is the maximum number of clients that are allowed at once, any number up to 4096 may be used, although the achievable number of simultaneous clients may be far lower and depends on the application. For additional details on the bandwidth parameters, see [method create_client]. + Create server that listens to connections via [code]port[/code]. The port needs to be an available, unused port between 0 and 65535. Note that ports below 1024 are privileged and may require elevated permissions depending on the platform. To change the interface the server listens on, use [method set_bind_ip]. The default IP is the wildcard [code]*[/code], which listens on all available interfaces. [code]max_clients[/code] is the maximum number of clients that are allowed at once, any number up to 4096 may be used, although the achievable number of simultaneous clients may be far lower and depends on the application. For additional details on the bandwidth parameters, see [method create_client]. Returns [code]OK[/code] if a server was created, [code]ERR_ALREADY_IN_USE[/code] if this NetworkedMultiplayerEnet instance already has an open connection (in which case you need to call [method close_connection] first) or [code]ERR_CANT_CREATE[/code] if the server could not be created. </description> </method> <method name="set_bind_ip"> diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub index a34a650a4d..8a7c2a773a 100644 --- a/modules/freetype/SCsub +++ b/modules/freetype/SCsub @@ -49,7 +49,6 @@ if env['builtin_freetype']: "src/pshinter/pshinter.c", "src/psnames/psnames.c", "src/raster/raster.c", - "src/sfnt/sfnt.c", "src/smooth/smooth.c", "src/truetype/truetype.c", "src/type1/type1.c", @@ -58,9 +57,18 @@ if env['builtin_freetype']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - # Include header for UWP to fix build issues - if "platform" in env and env["platform"] == "uwp": - env.Append(CCFLAGS=['/FI', '"modules/freetype/uwpdef.h"']) + sfnt = thirdparty_dir + 'src/sfnt/sfnt.c' + + if 'platform' in env: + if env['platform'] == 'uwp': + # Include header for UWP to fix build issues + env.Append(CCFLAGS=['/FI', '"modules/freetype/uwpdef.h"']) + elif env['platform'] == 'javascript': + # Forcibly undefine this macro so SIMD is not used in this file, + # since currently unsuported in WASM + sfnt = env.Object(sfnt, CPPFLAGS=['-U__OPTIMIZE__']) + + thirdparty_sources += [sfnt] env.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "/include"]) diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index db3787470f..5f72dca866 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -60,7 +60,7 @@ Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const Str "# var a = 2\n" + "# var b = \"textvar\"\n\n" + "func _ready():\n" + - "%TS%# Called every time the node is added to the scene.\n" + + "%TS%# Called when the node is added to the scene for the first time.\n" + "%TS%# Initialization here\n" + "%TS%pass\n\n" + "#func _process(delta):\n" + diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index ac7fd94141..435bc327dc 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -461,18 +461,21 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s } else if (!for_completion || FileAccess::exists(path)) { res = ResourceLoader::load(path); } - if (!res.is_valid()) { - _set_error("Can't preload resource at path: " + path); - return NULL; - } } else { if (!FileAccess::exists(path)) { _set_error("Can't preload resource at path: " + path); return NULL; + } else if (ScriptCodeCompletionCache::get_singleton()) { + res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path); } } + if (!res.is_valid()) { + _set_error("Can't preload resource at path: " + path); + return NULL; + } + if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { _set_error("Expected ')' after 'preload' path"); return NULL; diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index e91c1ebd85..869492232c 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -1269,9 +1269,10 @@ GridMapEditor::~GridMapEditor() { VisualServer::get_singleton()->free(grid_instance[i]); if (cursor_instance.is_valid()) VisualServer::get_singleton()->free(cursor_instance); - if (selection_level_instance[i].is_valid()) { + if (selection_level_instance[i].is_valid()) VisualServer::get_singleton()->free(selection_level_instance[i]); - } + if (selection_level_mesh[i].is_valid()) + VisualServer::get_singleton()->free(selection_level_mesh[i]); } VisualServer::get_singleton()->free(selection_mesh); diff --git a/modules/hdr/image_loader_hdr.cpp b/modules/hdr/image_loader_hdr.cpp index 3cc362b5d6..d592c19b97 100644 --- a/modules/hdr/image_loader_hdr.cpp +++ b/modules/hdr/image_loader_hdr.cpp @@ -42,14 +42,18 @@ Error ImageLoaderHDR::load_image(Ref<Image> p_image, FileAccess *f, bool p_force ERR_FAIL_COND_V(header != "#?RADIANCE" && header != "#?RGBE", ERR_FILE_UNRECOGNIZED); while (true) { - String format = f->get_token(); + String line = f->get_line(); ERR_FAIL_COND_V(f->eof_reached(), ERR_FILE_UNRECOGNIZED); - if (format.begins_with("FORMAT=") && format != "FORMAT=32-bit_rle_rgbe") { - ERR_EXPLAIN("Only 32-bit_rle_rgbe is supported for .hdr files."); - return ERR_FILE_UNRECOGNIZED; - } - if (format == "FORMAT=32-bit_rle_rgbe") + if (line == "") // empty line indicates end of header break; + if (line.begins_with("FORMAT=")) { // leave option to implement other commands + if (line != "FORMAT=32-bit_rle_rgbe") { + ERR_EXPLAIN("Only 32-bit_rle_rgbe is supported for HDR files."); + return ERR_FILE_UNRECOGNIZED; + } + } else if (!line.begins_with("#")) { // not comment + WARN_PRINTS("Ignoring unsupported header information in HDR : " + line); + } } String token = f->get_token(); diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 525b918b1f..1d0afa7f2d 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -282,6 +282,15 @@ void CSharpLanguage::get_string_delimiters(List<String> *p_delimiters) const { p_delimiters->push_back("@\" \""); // verbatim string literal } +static String get_base_class_name(const String &p_base_class_name, const String p_class_name) { + + String base_class = p_base_class_name; + if (p_class_name == base_class) { + base_class = "Godot." + base_class; + } + return base_class; +} + Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const { String script_template = "using " BINDINGS_NAMESPACE ";\n" @@ -308,7 +317,8 @@ Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const Strin "// }\n" "}\n"; - script_template = script_template.replace("%BASE_CLASS_NAME%", p_base_class_name) + String base_class_name = get_base_class_name(p_base_class_name, p_class_name); + script_template = script_template.replace("%BASE_CLASS_NAME%", base_class_name) .replace("%CLASS_NAME%", p_class_name); Ref<CSharpScript> script; @@ -327,12 +337,24 @@ bool CSharpLanguage::is_using_templates() { void CSharpLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) { String src = p_script->get_source_code(); - src = src.replace("%BASE%", p_base_class_name) + String base_class_name = get_base_class_name(p_base_class_name, p_class_name); + src = src.replace("%BASE%", base_class_name) .replace("%CLASS%", p_class_name) .replace("%TS%", _get_indentation()); p_script->set_source_code(src); } +String CSharpLanguage::validate_path(const String &p_path) const { + + String class_name = p_path.get_file().get_basename(); + List<String> keywords; + get_reserved_words(&keywords); + if (keywords.find(class_name)) { + return TTR("Class name can't be a reserved keyword"); + } + return ""; +} + Script *CSharpLanguage::create_script() const { return memnew(CSharpScript); diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index 1f609627de..8666149111 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -294,6 +294,7 @@ public: virtual bool is_using_templates(); virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script); /* TODO */ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const { return true; } + virtual String validate_path(const String &p_path) const; virtual Script *create_script() const; virtual bool has_named_classes() const; virtual bool supports_builtin_mode() const; diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 315c1e2f1c..a210b8e480 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -103,7 +103,7 @@ #define C_METHOD_MANAGED_TO_DICT C_NS_MONOMARSHAL "::mono_object_to_Dictionary" #define C_METHOD_MANAGED_FROM_DICT C_NS_MONOMARSHAL "::Dictionary_to_mono_object" -#define BINDINGS_GENERATOR_VERSION UINT32_C(1) +#define BINDINGS_GENERATOR_VERSION UINT32_C(2) const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN = "\t%0 %1_in = %1;\n"; @@ -730,7 +730,8 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str } output.push_back(INDENT1 "public "); - output.push_back(itype.is_singleton ? "static class " : "class "); + bool is_abstract = !ClassDB::can_instance(itype.name) && ClassDB::is_class_enabled(itype.name); // can_instance returns true if there's a constructor and the class is not 'disabled' + output.push_back(itype.is_singleton ? "static class " : (is_abstract ? "abstract class " : "class ")); output.push_back(itype.proxy_name); if (itype.is_singleton) { diff --git a/modules/mono/glue/cs_files/StringExtensions.cs b/modules/mono/glue/cs_files/StringExtensions.cs index 5c3ceff97d..cbc337ab19 100644 --- a/modules/mono/glue/cs_files/StringExtensions.cs +++ b/modules/mono/glue/cs_files/StringExtensions.cs @@ -287,7 +287,7 @@ namespace Godot if (sep == -1) return @base; - return @base + rs.substr(0, sep); + return @base + rs.Substr(0, sep); } // <summary> @@ -478,7 +478,7 @@ namespace Godot // </summary> public static bool IsValidIpAddress(this string instance) { - string[] ip = instance.split("."); + string[] ip = instance.Split("."); if (ip.Length != 4) return false; @@ -489,7 +489,7 @@ namespace Godot if (!n.IsValidInteger()) return false; - int val = n.to_int(); + int val = n.ToInt(); if (val < 0 || val > 255) return false; } @@ -571,7 +571,7 @@ namespace Godot // <summary> // Do a simple case insensitive expression match, using ? and * wildcards (see [method expr_match]). // </summary> - public static bool matchn(this string instance, string expr) + public static bool Matchn(this string instance, string expr) { return instance.ExprMatch(expr, false); } @@ -830,7 +830,7 @@ namespace Godot // <summary> // Split the string by a divisor string, return an array of the substrings. Example "One,Two,Three" will return ["One","Two","Three"] if split by ",". // </summary> - public static string[] split(this string instance, string divisor, bool allow_empty = true) + public static string[] Split(this string instance, string divisor, bool allow_empty = true) { return instance.Split(new string[] { divisor }, StringSplitOptions.RemoveEmptyEntries); } @@ -838,7 +838,7 @@ namespace Godot // <summary> // Split the string in floats by using a divisor string, return an array of the substrings. Example "1,2.5,3" will return [1,2.5,3] if split by ",". // </summary> - public static float[] split_floats(this string instance, string divisor, bool allow_empty = true) + public static float[] SplitFloats(this string instance, string divisor, bool allow_empty = true) { List<float> ret = new List<float>(); int from = 0; @@ -872,7 +872,7 @@ namespace Godot // <summary> // Return a copy of the string stripped of any non-printable character at the beginning and the end. The optional arguments are used to toggle stripping on the left and right edges respectively. // </summary> - public static string strip_edges(this string instance, bool left = true, bool right = true) + public static string StripEdges(this string instance, bool left = true, bool right = true) { if (left) { @@ -890,7 +890,7 @@ namespace Godot // <summary> // Return part of the string from the position [code]from[/code], with length [code]len[/code]. // </summary> - public static string substr(this string instance, int from, int len) + public static string Substr(this string instance, int from, int len) { return instance.Substring(from, len); } @@ -898,7 +898,7 @@ namespace Godot // <summary> // Convert the String (which is a character array) to PoolByteArray (which is an array of bytes). The conversion is speeded up in comparison to to_utf8() with the assumption that all the characters the String contains are only ASCII characters. // </summary> - public static byte[] to_ascii(this string instance) + public static byte[] ToAscii(this string instance) { return Encoding.ASCII.GetBytes(instance); } @@ -906,7 +906,7 @@ namespace Godot // <summary> // Convert a string, containing a decimal number, into a [code]float[/code]. // </summary> - public static float to_float(this string instance) + public static float ToFloat(this string instance) { return float.Parse(instance); } @@ -914,7 +914,7 @@ namespace Godot // <summary> // Convert a string, containing an integer number, into an [code]int[/code]. // </summary> - public static int to_int(this string instance) + public static int ToInt(this string instance) { return int.Parse(instance); } @@ -922,7 +922,7 @@ namespace Godot // <summary> // Return the string converted to lowercase. // </summary> - public static string to_lower(this string instance) + public static string ToLower(this string instance) { return instance.ToLower(); } @@ -930,7 +930,7 @@ namespace Godot // <summary> // Return the string converted to uppercase. // </summary> - public static string to_upper(this string instance) + public static string ToUpper(this string instance) { return instance.ToUpper(); } @@ -938,7 +938,7 @@ namespace Godot // <summary> // Convert the String (which is an array of characters) to PoolByteArray (which is an array of bytes). The conversion is a bit slower than to_ascii(), but supports all UTF-8 characters. Therefore, you should prefer this function over to_ascii(). // </summary> - public static byte[] to_utf8(this string instance) + public static byte[] ToUtf8(this string instance) { return Encoding.UTF8.GetBytes(instance); } @@ -946,7 +946,7 @@ namespace Godot // <summary> // Return a copy of the string with special characters escaped using the XML standard. // </summary> - public static string xml_escape(this string instance) + public static string XmlEscape(this string instance) { return SecurityElement.Escape(instance); } @@ -954,7 +954,7 @@ namespace Godot // <summary> // Return a copy of the string with escaped characters replaced by their meanings according to the XML standard. // </summary> - public static string xml_unescape(this string instance) + public static string XmlUnescape(this string instance) { return SecurityElement.FromString(instance).Text; } diff --git a/modules/mono/glue/cs_files/Transform.cs b/modules/mono/glue/cs_files/Transform.cs index 5214100d36..9853721f98 100644 --- a/modules/mono/glue/cs_files/Transform.cs +++ b/modules/mono/glue/cs_files/Transform.cs @@ -24,7 +24,7 @@ namespace Godot public Transform LookingAt(Vector3 target, Vector3 up) { Transform t = this; - t.set_look_at(origin, target, up); + t.SetLookAt(origin, target, up); return t; } @@ -43,7 +43,7 @@ namespace Godot return new Transform(basis.Scaled(scale), origin * scale); } - public void set_look_at(Vector3 eye, Vector3 target, Vector3 up) + public void SetLookAt(Vector3 eye, Vector3 target, Vector3 up) { // Make rotation matrix // Z vector diff --git a/modules/mono/glue/cs_files/VERSION.txt b/modules/mono/glue/cs_files/VERSION.txt index d00491fd7e..0cfbf08886 100755 --- a/modules/mono/glue/cs_files/VERSION.txt +++ b/modules/mono/glue/cs_files/VERSION.txt @@ -1 +1 @@ -1 +2 diff --git a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp b/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp index 16ebfa2832..c8acdb689a 100644 --- a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp +++ b/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp @@ -100,6 +100,7 @@ Error ResourceImporterOGGVorbis::import(const String &p_source_file, const Strin ogg_stream.instance(); ogg_stream->set_data(data); + ERR_FAIL_COND_V(!ogg_stream->get_data().size(), ERR_FILE_CORRUPT); ogg_stream->set_loop(loop); ogg_stream->set_loop_offset(loop_offset); diff --git a/modules/webm/SCsub b/modules/webm/SCsub index 2f1a28a54c..33561da098 100644 --- a/modules/webm/SCsub +++ b/modules/webm/SCsub @@ -18,6 +18,10 @@ thirdparty_libsimplewebm_sources = [thirdparty_libsimplewebm_dir + file for file env_webm.add_source_files(env.modules_sources, thirdparty_libsimplewebm_sources) env_webm.Append(CPPPATH=[thirdparty_libsimplewebm_dir, thirdparty_libsimplewebm_dir + "libwebm/"]) +# upstream uses c++11 +if (not env_webm.msvc): + env_webm.Append(CCFLAGS="-std=c++11") + # also requires libogg, libvorbis and libopus if env['builtin_libogg']: env_webm.Append(CPPPATH=["#thirdparty/libogg"]) diff --git a/platform/SCsub b/platform/SCsub index e624f8e90f..2019a30be7 100644 --- a/platform/SCsub +++ b/platform/SCsub @@ -18,11 +18,10 @@ for platform in env.platform_apis: reg_apis_inc += '\n' reg_apis += '}\n\n' unreg_apis += '}\n' -f = open_utf8('register_platform_apis.gen.cpp', 'w') -f.write(reg_apis_inc) -f.write(reg_apis) -f.write(unreg_apis) -f.close() +with open_utf8('register_platform_apis.gen.cpp', 'w') as f: + f.write(reg_apis_inc) + f.write(reg_apis) + f.write(unreg_apis) platform_sources.append('register_platform_apis.gen.cpp') lib = env.add_library('platform', platform_sources) diff --git a/platform/android/SCsub b/platform/android/SCsub index d2285a82dd..8c08289932 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -41,10 +41,8 @@ prog = None abspath = env.Dir(".").abspath -gradle_basein = open_utf8(abspath + "/build.gradle.template", "r") -gradle_baseout = open_utf8(abspath + "/java/build.gradle", "w") - -gradle_text = gradle_basein.read() +with open_utf8(abspath + "/build.gradle.template", "r") as gradle_basein: + gradle_text = gradle_basein.read() gradle_maven_flat_text = "" if len(env.android_flat_dirs) > 0: @@ -131,17 +129,19 @@ gradle_text = gradle_text.replace("$$GRADLE_DEFAULT_CONFIG$$", gradle_default_co gradle_text = gradle_text.replace("$$GRADLE_PLUGINS$$", gradle_plugins) gradle_text = gradle_text.replace("$$GRADLE_CLASSPATH$$", gradle_classpath) -gradle_baseout.write(gradle_text) -gradle_baseout.close() +with open_utf8(abspath + "/java/build.gradle", "w") as gradle_baseout: + gradle_baseout.write(gradle_text) + +with open_utf8(abspath + "/AndroidManifest.xml.template", "r") as pp_basein: + manifest = pp_basein.read() -pp_basein = open_utf8(abspath + "/AndroidManifest.xml.template", "r") -pp_baseout = open_utf8(abspath + "/java/AndroidManifest.xml", "w") -manifest = pp_basein.read() manifest = manifest.replace("$$ADD_APPLICATION_CHUNKS$$", env.android_manifest_chunk) manifest = manifest.replace("$$ADD_PERMISSION_CHUNKS$$", env.android_permission_chunk) manifest = manifest.replace("$$ADD_APPATTRIBUTE_CHUNKS$$", env.android_appattributes_chunk) -pp_baseout.write(manifest) + +with open_utf8(abspath + "/java/AndroidManifest.xml", "w") as pp_baseout: + pp_baseout.write(manifest) lib = env_android.add_shared_library("#bin/libgodot", [android_objects], SHLIBSUFFIX=env["SHLIBSUFFIX"]) diff --git a/platform/iphone/platform_config.h b/platform/iphone/platform_config.h index d205c7da83..d9fd61fb6e 100644 --- a/platform/iphone/platform_config.h +++ b/platform/iphone/platform_config.h @@ -30,6 +30,7 @@ #include <alloca.h> +#define GLES2_INCLUDE_H <ES2/gl.h> #define GLES3_INCLUDE_H <ES3/gl.h> #define PLATFORM_REFCOUNT diff --git a/platform/javascript/engine.js b/platform/javascript/engine.js index bca1851f40..8f22e41660 100644 --- a/platform/javascript/engine.js +++ b/platform/javascript/engine.js @@ -138,18 +138,6 @@ } var actualCanvas = this.rtenv.canvas; - var testContext = false; - var testCanvas; - try { - testCanvas = document.createElement('canvas'); - testContext = testCanvas.getContext('webgl2') || testCanvas.getContext('experimental-webgl2'); - } catch (e) {} - if (!testContext) { - throw new Error("WebGL 2 not available"); - } - testCanvas = null; - testContext = null; - // canvas can grab focus on click if (actualCanvas.tabIndex < 0) { actualCanvas.tabIndex = 0; @@ -273,6 +261,20 @@ Engine.RuntimeEnvironment = engine.RuntimeEnvironment; + Engine.isWebGLAvailable = function(majorVersion = 1) { + + var testContext = false; + try { + var testCanvas = document.createElement('canvas'); + if (majorVersion === 1) { + testContext = testCanvas.getContext('webgl') || testCanvas.getContet('experimental-webgl'); + } else if (majorVersion === 2) { + testContext = testCanvas.getContext('webgl2') || testCanvas.getContet('experimental-webgl2'); + } + } catch (e) {} + return !!testContext; + }; + Engine.load = function(newBasePath) { if (newBasePath !== undefined) basePath = getBasePath(newBasePath); diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index 5ae232d8f3..cbfe99ba2d 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -33,6 +33,7 @@ #include "core/engine.h" #include "core/io/file_access_buffered_fa.h" #include "dom_keys.h" +#include "drivers/gles2/rasterizer_gles2.h" #include "drivers/gles3/rasterizer_gles3.h" #include "drivers/unix/dir_access_unix.h" #include "drivers/unix/file_access_unix.h" @@ -57,12 +58,19 @@ static void dom2godot_mod(T emscripten_event_ptr, Ref<InputEventWithModifiers> g int OS_JavaScript::get_video_driver_count() const { - return 1; + return VIDEO_DRIVER_MAX; } const char *OS_JavaScript::get_video_driver_name(int p_driver) const { - return "GLES3"; + switch (p_driver) { + case VIDEO_DRIVER_GLES3: + return "GLES3"; + case VIDEO_DRIVER_GLES2: + return "GLES2"; + } + ERR_EXPLAIN("Invalid video driver index " + itos(p_driver)); + ERR_FAIL_V(NULL); } int OS_JavaScript::get_audio_driver_count() const { @@ -422,8 +430,21 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, emscripten_webgl_init_context_attributes(&attributes); attributes.alpha = false; attributes.antialias = false; - attributes.majorVersion = 2; + ERR_FAIL_INDEX_V(p_video_driver, VIDEO_DRIVER_MAX, ERR_INVALID_PARAMETER); + switch (p_video_driver) { + case VIDEO_DRIVER_GLES3: + attributes.majorVersion = 2; + RasterizerGLES3::register_config(); + RasterizerGLES3::make_current(); + break; + case VIDEO_DRIVER_GLES2: + attributes.majorVersion = 1; + RasterizerGLES2::register_config(); + RasterizerGLES2::make_current(); + break; + } EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(NULL, &attributes); + ERR_EXPLAIN("WebGL " + itos(attributes.majorVersion) + ".0 not available"); ERR_FAIL_COND_V(emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS, ERR_UNAVAILABLE); video_mode = p_desired; @@ -449,9 +470,6 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, AudioDriverManager::initialize(p_audio_driver); - RasterizerGLES3::register_config(); - RasterizerGLES3::make_current(); - print_line("Init VS"); visual_server = memnew(VisualServerRaster()); diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp index edd093afbb..db265812fa 100644 --- a/platform/osx/export/export.cpp +++ b/platform/osx/export/export.cpp @@ -496,7 +496,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p if (use_dmg()) { String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck"; Vector<SharedObject> shared_objects; - Error err = save_pack(p_preset, pack_path, &shared_objects); + err = save_pack(p_preset, pack_path, &shared_objects); // see if we can code sign our new package String identity = p_preset->get("codesign/identity"); @@ -504,7 +504,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p if (err == OK) { DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); for (int i = 0; i < shared_objects.size(); i++) { - da->copy(shared_objects[i].path, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file()); + err = da->copy(shared_objects[i].path, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file()); if (err == OK && identity != "") { err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file()); } @@ -549,7 +549,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p String pack_path = EditorSettings::get_singleton()->get_cache_dir().plus_file(pkg_name + ".pck"); Vector<SharedObject> shared_objects; - Error err = save_pack(p_preset, pack_path, &shared_objects); + err = save_pack(p_preset, pack_path, &shared_objects); if (err == OK) { zipOpenNewFileInZip(dst_pkg_zip, @@ -581,7 +581,9 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p } else { err = ERR_CANT_OPEN; } + } + if (err == OK) { //add shared objects for (int i = 0; i < shared_objects.size(); i++) { Vector<uint8_t> file = FileAccess::get_file_as_array(shared_objects[i].path); @@ -609,7 +611,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p zipClose(dst_pkg_zip, NULL); } - return OK; + return err; } bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py index f2c5c5d42e..3ee195e4f9 100644 --- a/platform/uwp/detect.py +++ b/platform/uwp/detect.py @@ -25,8 +25,11 @@ def can_build(): def get_opts(): + from SCons.Variables import BoolVariable return [ + ('msvc_version', 'MSVC version to use. Ignored if VCINSTALLDIR is set in shell env.', None), + BoolVariable('use_mingw', 'Use the Mingw compiler, even if MSVC is installed. Only used on Windows.', False), ] diff --git a/platform/windows/context_gl_win.cpp b/platform/windows/context_gl_win.cpp index 8b57fdd9ce..d312fbcb12 100644 --- a/platform/windows/context_gl_win.cpp +++ b/platform/windows/context_gl_win.cpp @@ -38,6 +38,8 @@ #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 #define WGL_CONTEXT_FLAGS_ARB 0x2094 #define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int *); @@ -153,6 +155,7 @@ Error ContextGL_Win::initialize() { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, //we want a 3.3 context WGL_CONTEXT_MINOR_VERSION_ARB, 3, //and it shall be forward compatible so that we can only use up to date functionality + WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB /*| _WGL_CONTEXT_DEBUG_BIT_ARB*/, 0 }; //zero indicates the end of the array diff --git a/platform/windows/detect.py b/platform/windows/detect.py index b8ee2cd19f..2ce55d98be 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -12,23 +12,17 @@ def get_name(): def can_build(): - if (os.name == "nt"): # Building natively on Windows - if (os.getenv("VCINSTALLDIR")): # MSVC + # If VCINSTALLDIR is set in the OS environ, use traditional Godot logic to set up MSVC + if (os.getenv("VCINSTALLDIR")): # MSVC, manual setup return True - print("MSVC not detected (no VCINSTALLDIR environment variable), attempting MinGW.") - mingw32 = "" - mingw64 = "" - if (os.getenv("MINGW32_PREFIX")): - mingw32 = os.getenv("MINGW32_PREFIX") - if (os.getenv("MINGW64_PREFIX")): - mingw64 = os.getenv("MINGW64_PREFIX") - - test = "gcc --version > NUL 2>&1" - if (os.system(test) == 0 or os.system(mingw32 + test) == 0 or os.system(mingw64 + test) == 0): - return True + # Otherwise, let SCons find MSVC if installed, or else Mingw. + # Since we're just returning True here, if there's no compiler + # installed, we'll get errors when it tries to build with the + # null compiler. + return True if (os.name == "posix"): # Cross-compiling with MinGW-w64 (old MinGW32 is not supported) @@ -70,6 +64,8 @@ def get_opts(): ('target_win_version', 'Targeted Windows version, >= 0x0601 (Windows 7)', '0x0601'), EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')), BoolVariable('separate_debug_symbols', 'Create a separate file with the debug symbols', False), + ('msvc_version', 'MSVC version to use. Ignored if VCINSTALLDIR is set in shell env.', None), + BoolVariable('use_mingw', 'Use the Mingw compiler, even if MSVC is installed. Only used on Windows.', False), ] @@ -98,192 +94,253 @@ def build_res_file(target, source, env): return 0 -def configure(env): - - env.Append(CPPPATH=['#platform/windows']) - - if (os.name == "nt" and os.getenv("VCINSTALLDIR")): # MSVC +def setup_msvc_manual(env): + """Set up env to use MSVC manually, using VCINSTALLDIR""" + if (env["bits"] != "default"): + print(""" + Bits argument is not supported for MSVC compilation. Architecture depends on the Native/Cross Compile Tools Prompt/Developer Console + (or Visual Studio settings) that is being used to run SCons. As a consequence, bits argument is disabled. Run scons again without bits + argument (example: scons p=windows) and SCons will attempt to detect what MSVC compiler will be executed and inform you. + """) + raise SCons.Errors.UserError("Bits argument should not be used when using VCINSTALLDIR") + + # Force bits arg + # (Actually msys2 mingw can support 64-bit, we could detect that) + env["bits"] = "32" + env["x86_libtheora_opt_vc"] = True + + # find compiler manually + compiler_version_str = methods.detect_visual_c_compiler_version(env['ENV']) + print("Found MSVC compiler: " + compiler_version_str) + + # If building for 64bit architecture, disable assembly optimisations for 32 bit builds (theora as of writing)... vc compiler for 64bit can not compile _asm + if(compiler_version_str == "amd64" or compiler_version_str == "x86_amd64"): + env["bits"] = "64" + env["x86_libtheora_opt_vc"] = False + print("Compiled program architecture will be a 64 bit executable (forcing bits=64).") + elif (compiler_version_str == "x86" or compiler_version_str == "amd64_x86"): + print("Compiled program architecture will be a 32 bit executable. (forcing bits=32).") + else: + print("Failed to manually detect MSVC compiler architecture version... Defaulting to 32bit executable settings (forcing bits=32). Compilation attempt will continue, but SCons can not detect for what architecture this build is compiled for. You should check your settings/compilation setup, or avoid setting VCINSTALLDIR.") + +def setup_msvc_auto(env): + """Set up MSVC using SCons's auto-detection logic""" + + # If MSVC_VERSION is set by SCons, we know MSVC is installed. + # But we may want a different version or target arch. + + # The env may have already been set up with default MSVC tools, so + # reset a few things so we can set it up with the tools we want. + # (Ideally we'd decide on the tool config before configuring any + # environment, and just set the env up once, but this function runs + # on an existing env so this is the simplest way.) + env['MSVC_SETUP_RUN'] = False # Need to set this to re-run the tool + env['MSVS_VERSION'] = None + env['MSVC_VERSION'] = None + env['TARGET_ARCH'] = None + if env['bits'] != 'default': + env['TARGET_ARCH'] = {'32': 'x86', '64': 'x86_64'}[env['bits']] + if env.has_key('msvc_version'): + env['MSVC_VERSION'] = env['msvc_version'] + env.Tool('msvc') + env.Tool('mssdk') # we want the MS SDK + # Note: actual compiler version can be found in env['MSVC_VERSION'], e.g. "14.1" for VS2015 + # Get actual target arch into bits (it may be "default" at this point): + if env['TARGET_ARCH'] in ('amd64', 'x86_64'): + env['bits'] = 64 + else: + env['bits'] = 32 + print(" Found MSVC version %s, arch %s, bits=%s" % (env['MSVC_VERSION'], env['TARGET_ARCH'], env['bits'])) + if env['TARGET_ARCH'] in ('amd64', 'x86_64'): + env["x86_libtheora_opt_vc"] = False + +def setup_mingw(env): + """Set up env for use with mingw""" + # Nothing to do here + print("Using Mingw") + pass + +def configure_msvc(env, manual_msvc_config): + """Configure env to work with MSVC""" + + # Build type + + if (env["target"] == "release"): + env.Append(CCFLAGS=['/O2']) + env.Append(LINKFLAGS=['/SUBSYSTEM:WINDOWS']) + env.Append(LINKFLAGS=['/ENTRY:mainCRTStartup']) + + elif (env["target"] == "release_debug"): + env.Append(CCFLAGS=['/O2']) + env.AppendUnique(CPPDEFINES = ['DEBUG_ENABLED']) + env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) + + elif (env["target"] == "debug_release"): + env.Append(CCFLAGS=['/Z7', '/Od']) + env.Append(LINKFLAGS=['/DEBUG']) + env.Append(LINKFLAGS=['/SUBSYSTEM:WINDOWS']) + env.Append(LINKFLAGS=['/ENTRY:mainCRTStartup']) + + elif (env["target"] == "debug"): + env.AppendUnique(CCFLAGS=['/Z7', '/Od', '/EHsc']) + env.AppendUnique(CPPDEFINES = ['DEBUG_ENABLED', 'DEBUG_MEMORY_ENABLED', + 'D3D_DEBUG_INFO']) + env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) + env.Append(LINKFLAGS=['/DEBUG']) + + ## Compile/link flags + + env.AppendUnique(CCFLAGS=['/MT', '/Gd', '/GR', '/nologo']) + env.AppendUnique(CXXFLAGS=['/TP']) # assume all sources are C++ + if manual_msvc_config: # should be automatic if SCons found it + env.Append(CPPPATH=[os.getenv("WindowsSdkDir") + "/Include"]) + + env.AppendUnique(CPPDEFINES = ['WINDOWS_ENABLED', 'OPENGL_ENABLED', + 'RTAUDIO_ENABLED', 'WASAPI_ENABLED', + 'TYPED_METHOD_BIND', 'WIN32', 'MSVC', + {'WINVER' : '$target_win_version', + '_WIN32_WINNT': '$target_win_version'}]) + if env["bits"] == "64": + env.AppendUnique(CPPDEFINES=['_WIN64']) + + ## Libs + + LIBS = ['winmm', 'opengl32', 'dsound', 'kernel32', 'ole32', 'oleaut32', + 'user32', 'gdi32', 'IPHLPAPI', 'Shlwapi', 'wsock32', 'Ws2_32', + 'shell32', 'advapi32', 'dinput8', 'dxguid', 'imm32', 'bcrypt'] + env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS]) + + if manual_msvc_config: + env.Append(LIBPATH=[os.getenv("WindowsSdkDir") + "/Lib"]) - env['ENV']['TMP'] = os.environ['TMP'] + ## LTO - ## Build type - - if (env["target"] == "release"): - env.Append(CCFLAGS=['/O2']) - env.Append(LINKFLAGS=['/SUBSYSTEM:WINDOWS']) - env.Append(LINKFLAGS=['/ENTRY:mainCRTStartup']) - - elif (env["target"] == "release_debug"): - env.Append(CCFLAGS=['/O2', '/DDEBUG_ENABLED']) - env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) - - elif (env["target"] == "debug_release"): - env.Append(CCFLAGS=['/Z7', '/Od']) - env.Append(LINKFLAGS=['/DEBUG']) - env.Append(LINKFLAGS=['/SUBSYSTEM:WINDOWS']) - env.Append(LINKFLAGS=['/ENTRY:mainCRTStartup']) - - elif (env["target"] == "debug"): - env.Append(CCFLAGS=['/Z7', '/DDEBUG_ENABLED', '/DDEBUG_MEMORY_ENABLED', '/DD3D_DEBUG_INFO', '/Od', '/EHsc']) - env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) - env.Append(LINKFLAGS=['/DEBUG']) - - ## Architecture - - # Note: this detection/override code from here onward should be here instead of in SConstruct because it's platform and compiler specific (MSVC/Windows) - if (env["bits"] != "default"): - print("Error: bits argument is disabled for MSVC") - print(""" - Bits argument is not supported for MSVC compilation. Architecture depends on the Native/Cross Compile Tools Prompt/Developer Console - (or Visual Studio settings) that is being used to run SCons. As a consequence, bits argument is disabled. Run scons again without bits - argument (example: scons p=windows) and SCons will attempt to detect what MSVC compiler will be executed and inform you. - """) - sys.exit() - - # Forcing bits argument because MSVC does not have a flag to set this through SCons... it's different compilers (cl.exe's) called from the proper command prompt - # that decide the architecture that is build for. Scons can only detect the os.getenviron (because vsvarsall.bat sets a lot of stuff for cl.exe to work with) - env["bits"] = "32" - env["x86_libtheora_opt_vc"] = True - - ## Compiler configuration - - env['ENV'] = os.environ - # This detection function needs the tools env (that is env['ENV'], not SCons's env), and that is why it's this far below in the code - compiler_version_str = methods.detect_visual_c_compiler_version(env['ENV']) - - print("Detected MSVC compiler: " + compiler_version_str) - # If building for 64bit architecture, disable assembly optimisations for 32 bit builds (theora as of writing)... vc compiler for 64bit can not compile _asm - if(compiler_version_str == "amd64" or compiler_version_str == "x86_amd64"): - env["bits"] = "64" - env["x86_libtheora_opt_vc"] = False - print("Compiled program architecture will be a 64 bit executable (forcing bits=64).") - elif (compiler_version_str == "x86" or compiler_version_str == "amd64_x86"): - print("Compiled program architecture will be a 32 bit executable. (forcing bits=32).") + if (env["use_lto"]): + env.AppendUnique(CCFLAGS=['/GL']) + env.AppendUnique(ARFLAGS=['/LTCG']) + if env["progress"]: + env.AppendUnique(LINKFLAGS=['/LTCG:STATUS']) else: - print("Failed to detect MSVC compiler architecture version... Defaulting to 32bit executable settings (forcing bits=32). Compilation attempt will continue, but SCons can not detect for what architecture this build is compiled for. You should check your settings/compilation setup.") + env.AppendUnique(LINKFLAGS=['/LTCG']) - ## Compile flags + if manual_msvc_config: + env.Append(CPPPATH=[p for p in os.getenv("INCLUDE").split(";")]) + env.Append(LIBPATH=[p for p in os.getenv("LIB").split(";")]) - env.Append(CCFLAGS=['/MT', '/Gd', '/GR', '/nologo']) - env.Append(CXXFLAGS=['/TP']) - env.Append(CPPFLAGS=['/DMSVC', '/GR', ]) - env.Append(CCFLAGS=['/I' + os.getenv("WindowsSdkDir") + "/Include"]) + # Incremental linking fix + env['BUILDERS']['ProgramOriginal'] = env['BUILDERS']['Program'] + env['BUILDERS']['Program'] = methods.precious_program - env.Append(CCFLAGS=['/DWINDOWS_ENABLED']) - env.Append(CCFLAGS=['/DOPENGL_ENABLED']) - env.Append(CCFLAGS=['/DRTAUDIO_ENABLED']) - env.Append(CCFLAGS=['/DWASAPI_ENABLED']) - env.Append(CCFLAGS=['/DTYPED_METHOD_BIND']) - env.Append(CCFLAGS=['/DWIN32']) - env.Append(CCFLAGS=['/DWINVER=%s' % env['target_win_version'], '/D_WIN32_WINNT=%s' % env['target_win_version']]) - if env["bits"] == "64": - env.Append(CCFLAGS=['/D_WIN64']) +def configure_mingw(env): + # Workaround for MinGW. See: + # http://www.scons.org/wiki/LongCmdLinesOnWin32 + env.use_windows_spawn_fix() - LIBS = ['winmm', 'opengl32', 'dsound', 'kernel32', 'ole32', 'oleaut32', 'user32', 'gdi32', 'IPHLPAPI', 'Shlwapi', 'wsock32', 'Ws2_32', 'shell32', 'advapi32', 'dinput8', 'dxguid', 'imm32', 'bcrypt'] - env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS]) + ## Build type - env.Append(LIBPATH=[os.getenv("WindowsSdkDir") + "/Lib"]) + if (env["target"] == "release"): + env.Append(CCFLAGS=['-msse2']) - if (os.getenv("VCINSTALLDIR")): - VC_PATH = os.getenv("VCINSTALLDIR") + if (env["bits"] == "64"): + env.Append(CCFLAGS=['-O3']) else: - VC_PATH = "" + env.Append(CCFLAGS=['-O2']) - if (env["use_lto"]): - env.Append(CCFLAGS=['/GL']) - env.Append(ARFLAGS=['/LTCG']) - if env["progress"]: - env.Append(LINKFLAGS=['/LTCG:STATUS']) - else: - env.Append(LINKFLAGS=['/LTCG']) + env.Append(LINKFLAGS=['-Wl,--subsystem,windows']) - env.Append(CCFLAGS=["/I" + p for p in os.getenv("INCLUDE").split(";")]) - env.Append(LIBPATH=[p for p in os.getenv("LIB").split(";")]) + if (env["debug_symbols"] == "yes"): + env.Prepend(CCFLAGS=['-g1']) + if (env["debug_symbols"] == "full"): + env.Prepend(CCFLAGS=['-g2']) - # Incremental linking fix - env['BUILDERS']['ProgramOriginal'] = env['BUILDERS']['Program'] - env['BUILDERS']['Program'] = methods.precious_program + elif (env["target"] == "release_debug"): + env.Append(CCFLAGS=['-O2', '-DDEBUG_ENABLED']) + if (env["debug_symbols"] == "yes"): + env.Prepend(CCFLAGS=['-g1']) + if (env["debug_symbols"] == "full"): + env.Prepend(CCFLAGS=['-g2']) - else: # MinGW - - # Workaround for MinGW. See: - # http://www.scons.org/wiki/LongCmdLinesOnWin32 - env.use_windows_spawn_fix() - - ## Build type + elif (env["target"] == "debug"): + env.Append(CCFLAGS=['-g3', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) - if (env["target"] == "release"): - env.Append(CCFLAGS=['-msse2']) + ## Compiler configuration - if (env["bits"] == "64"): - env.Append(CCFLAGS=['-O3']) - else: - env.Append(CCFLAGS=['-O2']) + if (os.name == "nt"): + env['ENV']['TMP'] = os.environ['TMP'] # way to go scons, you can be so stupid sometimes + else: + env["PROGSUFFIX"] = env["PROGSUFFIX"] + ".exe" # for linux cross-compilation - env.Append(LINKFLAGS=['-Wl,--subsystem,windows']) + if (env["bits"] == "default"): + if (os.name == "nt"): + env["bits"] = "64" if "PROGRAMFILES(X86)" in os.environ else "32" + else: # default to 64-bit on Linux + env["bits"] = "64" - if (env["debug_symbols"] == "yes"): - env.Prepend(CCFLAGS=['-g1']) - if (env["debug_symbols"] == "full"): - env.Prepend(CCFLAGS=['-g2']) + mingw_prefix = "" - elif (env["target"] == "release_debug"): - env.Append(CCFLAGS=['-O2', '-DDEBUG_ENABLED']) - if (env["debug_symbols"] == "yes"): - env.Prepend(CCFLAGS=['-g1']) - if (env["debug_symbols"] == "full"): - env.Prepend(CCFLAGS=['-g2']) + if (env["bits"] == "32"): + env.Append(LINKFLAGS=['-static']) + env.Append(LINKFLAGS=['-static-libgcc']) + env.Append(LINKFLAGS=['-static-libstdc++']) + mingw_prefix = env["mingw_prefix_32"] + else: + env.Append(LINKFLAGS=['-static']) + mingw_prefix = env["mingw_prefix_64"] - elif (env["target"] == "debug"): - env.Append(CCFLAGS=['-g3', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) + env["CC"] = mingw_prefix + "gcc" + env['AS'] = mingw_prefix + "as" + env['CXX'] = mingw_prefix + "g++" + env['AR'] = mingw_prefix + "gcc-ar" + env['RANLIB'] = mingw_prefix + "gcc-ranlib" + env['LINK'] = mingw_prefix + "g++" + env["x86_libtheora_opt_gcc"] = True - ## Compiler configuration + if env['use_lto']: + env.Append(CCFLAGS=['-flto']) + env.Append(LINKFLAGS=['-flto=' + str(env.GetOption("num_jobs"))]) - if (os.name == "nt"): - env['ENV']['TMP'] = os.environ['TMP'] # way to go scons, you can be so stupid sometimes - else: - env["PROGSUFFIX"] = env["PROGSUFFIX"] + ".exe" # for linux cross-compilation - if (env["bits"] == "default"): - if (os.name == "nt"): - env["bits"] = "64" if "PROGRAMFILES(X86)" in os.environ else "32" - else: # default to 64-bit on Linux - env["bits"] = "64" + ## Compile flags - mingw_prefix = "" + env.Append(CCFLAGS=['-DWINDOWS_ENABLED', '-mwindows']) + env.Append(CCFLAGS=['-DOPENGL_ENABLED']) + env.Append(CCFLAGS=['-DRTAUDIO_ENABLED']) + env.Append(CCFLAGS=['-DWASAPI_ENABLED']) + env.Append(CCFLAGS=['-DWINVER=%s' % env['target_win_version'], '-D_WIN32_WINNT=%s' % env['target_win_version']]) + env.Append(LIBS=['mingw32', 'opengl32', 'dsound', 'ole32', 'd3d9', 'winmm', 'gdi32', 'iphlpapi', 'shlwapi', 'wsock32', 'ws2_32', 'kernel32', 'oleaut32', 'dinput8', 'dxguid', 'ksuser', 'imm32', 'bcrypt']) - if (env["bits"] == "32"): - env.Append(LINKFLAGS=['-static']) - env.Append(LINKFLAGS=['-static-libgcc']) - env.Append(LINKFLAGS=['-static-libstdc++']) - mingw_prefix = env["mingw_prefix_32"] - else: - env.Append(LINKFLAGS=['-static']) - mingw_prefix = env["mingw_prefix_64"] + env.Append(CPPFLAGS=['-DMINGW_ENABLED']) - env["CC"] = mingw_prefix + "gcc" - env['AS'] = mingw_prefix + "as" - env['CXX'] = mingw_prefix + "g++" - env['AR'] = mingw_prefix + "gcc-ar" - env['RANLIB'] = mingw_prefix + "gcc-ranlib" - env['LINK'] = mingw_prefix + "g++" - env["x86_libtheora_opt_gcc"] = True + # resrc + env.Append(BUILDERS={'RES': env.Builder(action=build_res_file, suffix='.o', src_suffix='.rc')}) - if env['use_lto']: - env.Append(CCFLAGS=['-flto']) - env.Append(LINKFLAGS=['-flto=' + str(env.GetOption("num_jobs"))]) +def configure(env): + # At this point the env has been set up with basic tools/compilers. + env.Append(CPPPATH=['#platform/windows']) + print("Configuring for Windows: target=%s, bits=%s" % (env['target'], env['bits'])) - ## Compile flags + if (os.name == "nt"): + env['ENV'] = os.environ # this makes build less repeatable, but simplifies some things + env['ENV']['TMP'] = os.environ['TMP'] - env.Append(CCFLAGS=['-DWINDOWS_ENABLED', '-mwindows']) - env.Append(CCFLAGS=['-DOPENGL_ENABLED']) - env.Append(CCFLAGS=['-DRTAUDIO_ENABLED']) - env.Append(CCFLAGS=['-DWASAPI_ENABLED']) - env.Append(CCFLAGS=['-DWINVER=%s' % env['target_win_version'], '-D_WIN32_WINNT=%s' % env['target_win_version']]) - env.Append(LIBS=['mingw32', 'opengl32', 'dsound', 'ole32', 'd3d9', 'winmm', 'gdi32', 'iphlpapi', 'shlwapi', 'wsock32', 'ws2_32', 'kernel32', 'oleaut32', 'dinput8', 'dxguid', 'ksuser', 'imm32', 'bcrypt']) + # First figure out which compiler, version, and target arch we're using + if os.getenv("VCINSTALLDIR"): + # Manual setup of MSVC + setup_msvc_manual(env) + env.msvc = True + manual_msvc_config = True + elif env.get('MSVC_VERSION', ''): + setup_msvc_auto(env) + env.msvc = True + manual_msvc_config = False + else: + setup_mingw(env) + env.msvc = False - env.Append(CPPFLAGS=['-DMINGW_ENABLED']) + # Now set compiler/linker flags + if env.msvc: + configure_msvc(env, manual_msvc_config) - # resrc - env.Append(BUILDERS={'RES': env.Builder(action=build_res_file, suffix='.o', src_suffix='.rc')}) + else: # MinGW + configure_mingw(env) diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 910010187d..13fe781ff3 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -30,6 +30,7 @@ #include "os_windows.h" +#include "drivers/gles2/rasterizer_gles2.h" #include "drivers/gles3/rasterizer_gles3.h" #include "drivers/windows/dir_access_windows.h" #include "drivers/windows/file_access_windows.h" @@ -1069,12 +1070,19 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int } #if defined(OPENGL_ENABLED) - gl_context = memnew(ContextGL_Win(hWnd, true)); - gl_context->initialize(); + if (p_video_driver == VIDEO_DRIVER_GLES2) { + gl_context = memnew(ContextGL_Win(hWnd, false)); + gl_context->initialize(); - RasterizerGLES3::register_config(); + RasterizerGLES2::register_config(); + RasterizerGLES2::make_current(); + } else { + gl_context = memnew(ContextGL_Win(hWnd, true)); + gl_context->initialize(); - RasterizerGLES3::make_current(); + RasterizerGLES3::register_config(); + RasterizerGLES3::make_current(); + } gl_context->set_use_vsync(video_mode.use_vsync); #endif diff --git a/platform/x11/context_gl_x11.cpp b/platform/x11/context_gl_x11.cpp index 7a659a2734..1a7cbc0d6d 100644 --- a/platform/x11/context_gl_x11.cpp +++ b/platform/x11/context_gl_x11.cpp @@ -170,6 +170,7 @@ Error ContextGL_X11::initialize() { static int context_attribs[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, 3, + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB /*|GLX_CONTEXT_DEBUG_BIT_ARB*/, None }; diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp index 978fb379ac..7e2026d225 100644 --- a/scene/2d/collision_polygon_2d.cpp +++ b/scene/2d/collision_polygon_2d.cpp @@ -175,7 +175,8 @@ void CollisionPolygon2D::_notification(int p_what) { Vector2 p = polygon[i]; Vector2 n = polygon[(i + 1) % polygon.size()]; - draw_line(p, n, Color(0.9, 0.2, 0.0, 0.8), 3); + // draw line with width <= 1, so it does not scale with zoom and break pixel exact editing + draw_line(p, n, Color(0.9, 0.2, 0.0, 0.8), 1); } #define DEBUG_DECOMPOSE #if defined(TOOLS_ENABLED) && defined(DEBUG_DECOMPOSE) diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp index 0dd02a982c..67f016ae79 100644 --- a/scene/2d/sprite.cpp +++ b/scene/2d/sprite.cpp @@ -121,7 +121,15 @@ void Sprite::set_texture(const Ref<Texture> &p_texture) { if (p_texture == texture) return; + + if (texture.is_valid()) + texture->remove_change_receptor(this); + texture = p_texture; + + if (texture.is_valid()) + texture->add_change_receptor(this); + update(); emit_signal("texture_changed"); item_rect_changed(); @@ -281,13 +289,39 @@ bool Sprite::_edit_is_selected_on_click(const Point2 &p_point, double p_toleranc Rect2 src_rect, dst_rect; bool filter_clip; _get_rects(src_rect, dst_rect, filter_clip); + dst_rect.size = dst_rect.size.abs(); if (!dst_rect.has_point(p_point)) return false; - Vector2 q = ((p_point - dst_rect.position) / dst_rect.size) * src_rect.size + src_rect.position; + Vector2 q = (p_point - dst_rect.position) / dst_rect.size; + if (hflip) + q.x = 1.0f - q.x; + if (vflip) + q.y = 1.0f - q.y; + q = q * src_rect.size + src_rect.position; + + Ref<Image> image; + Ref<AtlasTexture> atlasTexture = texture; + if (atlasTexture.is_null()) { + image = texture->get_data(); + } else { + ERR_FAIL_COND_V(atlasTexture->get_atlas().is_null(), false); + + image = atlasTexture->get_atlas()->get_data(); + + Rect2 region = atlasTexture->get_region(); + Rect2 margin = atlasTexture->get_margin(); + + q -= margin.position; + + if ((q.x > region.size.width) || (q.y > region.size.height)) { + return false; + } + + q += region.position; + } - Ref<Image> image = texture->get_data(); ERR_FAIL_COND_V(image.is_null(), false); image->lock(); @@ -336,6 +370,15 @@ void Sprite::_validate_property(PropertyInfo &property) const { } } +void Sprite::_changed_callback(Object *p_changed, const char *p_prop) { + + // Changes to the texture need to trigger an update to make + // the editor redraw the sprite with the updated texture. + if (texture.is_valid() && texture.ptr() == p_changed) { + update(); + } +} + void Sprite::_bind_methods() { ClassDB::bind_method(D_METHOD("set_texture", "texture"), &Sprite::set_texture); @@ -410,3 +453,8 @@ Sprite::Sprite() { vframes = 1; hframes = 1; } + +Sprite::~Sprite() { + if (texture.is_valid()) + texture->remove_change_receptor(this); +} diff --git a/scene/2d/sprite.h b/scene/2d/sprite.h index dd3719099f..abd04515ec 100644 --- a/scene/2d/sprite.h +++ b/scene/2d/sprite.h @@ -64,6 +64,8 @@ protected: virtual void _validate_property(PropertyInfo &property) const; + virtual void _changed_callback(Object *p_changed, const char *p_prop); + public: virtual Dictionary _edit_get_state() const; virtual void _edit_set_state(const Dictionary &p_state); @@ -113,6 +115,7 @@ public: Rect2 get_rect() const; Sprite(); + ~Sprite(); }; #endif // SPRITE_H diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 2aa55e2825..b602839b99 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -142,16 +142,20 @@ void TileMap::_update_quadrant_transform() { void TileMap::set_tileset(const Ref<TileSet> &p_tileset) { - if (tile_set.is_valid()) + if (tile_set.is_valid()) { tile_set->disconnect("changed", this, "_recreate_quadrants"); + tile_set->remove_change_receptor(this); + } _clear_quadrants(); tile_set = p_tileset; - if (tile_set.is_valid()) + if (tile_set.is_valid()) { tile_set->connect("changed", this, "_recreate_quadrants"); - else + tile_set->add_change_receptor(this); + } else { clear(); + } _recreate_quadrants(); emit_signal("settings_changed"); @@ -830,6 +834,16 @@ void TileMap::update_dirty_bitmask() { } } +void TileMap::fix_invalid_tiles() { + + for (Map<PosKey, Cell>::Element *E = tile_map.front(); E; E = E->next()) { + + if (!tile_set->has_tile(get_cell(E->key().x, E->key().y))) { + set_cell(E->key().x, E->key().y, INVALID_CELL); + } + } +} + int TileMap::get_cell(int p_x, int p_y) const { PosKey pk(p_x, p_y); @@ -1515,6 +1529,7 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("is_cell_y_flipped", "x", "y"), &TileMap::is_cell_y_flipped); ClassDB::bind_method(D_METHOD("is_cell_transposed", "x", "y"), &TileMap::is_cell_transposed); + ClassDB::bind_method(D_METHOD("fix_invalid_tiles"), &TileMap::fix_invalid_tiles); ClassDB::bind_method(D_METHOD("clear"), &TileMap::clear); ClassDB::bind_method(D_METHOD("get_used_cells"), &TileMap::get_used_cells); @@ -1573,6 +1588,12 @@ void TileMap::_bind_methods() { BIND_ENUM_CONSTANT(TILE_ORIGIN_BOTTOM_LEFT); } +void TileMap::_changed_callback(Object *p_changed, const char *p_prop) { + if (tile_set.is_valid() && tile_set.ptr() == p_changed) { + emit_signal("settings_changed"); + } +} + TileMap::TileMap() { rect_cache_dirty = true; @@ -1601,5 +1622,8 @@ TileMap::TileMap() { TileMap::~TileMap() { + if (tile_set.is_valid()) + tile_set->remove_change_receptor(this); + clear(); } diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 973e527b42..587bd3b684 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -217,6 +217,8 @@ protected: void _notification(int p_what); static void _bind_methods(); + virtual void _changed_callback(Object *p_changed, const char *p_prop); + public: enum { INVALID_CELL = -1 @@ -308,6 +310,7 @@ public: void set_clip_uv(bool p_enable); bool get_clip_uv() const; + void fix_invalid_tiles(); void clear(); TileMap(); diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 9db4a5fb04..04e7d5cc10 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -1010,6 +1010,7 @@ void AnimationPlayer::stop(bool p_reset) { c.blend.clear(); if (p_reset) { c.current.from = NULL; + c.current.speed_scale = 1; } _set_process(false); queued.clear(); diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 30bcc48149..31be18612f 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -77,8 +77,7 @@ void ColorPicker::_notification(int p_what) { void ColorPicker::set_focus_on_line_edit() { - c_text->grab_focus(); - c_text->select(); + c_text->call_deferred("grab_focus"); } void ColorPicker::_update_controls() { @@ -159,14 +158,16 @@ void ColorPicker::_update_color() { updating = true; for (int i = 0; i < 4; i++) { - scroll[i]->set_max(255); scroll[i]->set_step(0.01); if (raw_mode_enabled) { + scroll[i]->set_max(100); if (i == 3) scroll[i]->set_max(1); scroll[i]->set_value(color.components[i]); } else { - scroll[i]->set_value(color.components[i] * 255); + const int byte_value = color.components[i] * 255; + scroll[i]->set_max(next_power_of_2(MAX(255, byte_value)) - 1); + scroll[i]->set_value(byte_value); } } @@ -242,6 +243,7 @@ bool ColorPicker::is_raw_mode() const { } void ColorPicker::_update_text_value() { + bool visible = true; if (text_is_constructor) { String t = "Color(" + String::num(color.r) + "," + String::num(color.g) + "," + String::num(color.b); if (edit_alpha && color.a < 1) @@ -250,8 +252,13 @@ void ColorPicker::_update_text_value() { t += ")"; c_text->set_text(t); } else { - c_text->set_text(color.to_html(edit_alpha && color.a < 1)); + if (color.r > 1 || color.g > 1 || color.b > 1 || color.r < 0 || color.g < 0 || color.b < 0) { + visible = false; + } else { + c_text->set_text(color.to_html(edit_alpha && color.a < 1)); + } } + c_text->set_visible(visible); } void ColorPicker::_sample_draw() { @@ -462,6 +469,31 @@ void ColorPicker::_screen_pick_pressed() { screen->show_modal(); } +void ColorPicker::_focus_enter() { + if (c_text->has_focus()) { + c_text->select_all(); + return; + } + for (int i = 0; i < 4; i++) { + if (values[i]->get_line_edit()->has_focus()) { + values[i]->get_line_edit()->select_all(); + break; + } + } +} + +void ColorPicker::_focus_exit() { + for (int i = 0; i < 4; i++) { + values[i]->get_line_edit()->select(0, 0); + } + c_text->select(0, 0); +} + +void ColorPicker::_html_focus_exit() { + _html_entered(c_text->get_text()); + _focus_exit(); +} + void ColorPicker::_bind_methods() { ClassDB::bind_method(D_METHOD("set_pick_color", "color"), &ColorPicker::set_pick_color); @@ -483,6 +515,9 @@ void ColorPicker::_bind_methods() { ClassDB::bind_method(D_METHOD("_w_input"), &ColorPicker::_w_input); ClassDB::bind_method(D_METHOD("_preset_input"), &ColorPicker::_preset_input); ClassDB::bind_method(D_METHOD("_screen_input"), &ColorPicker::_screen_input); + ClassDB::bind_method(D_METHOD("_focus_enter"), &ColorPicker::_focus_enter); + ClassDB::bind_method(D_METHOD("_focus_exit"), &ColorPicker::_focus_exit); + ClassDB::bind_method(D_METHOD("_html_focus_exit"), &ColorPicker::_html_focus_exit); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_pick_color", "get_pick_color"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha"); @@ -559,11 +594,14 @@ ColorPicker::ColorPicker() : scroll[i] = memnew(HSlider); scroll[i]->set_v_size_flags(SIZE_SHRINK_CENTER); + scroll[i]->set_focus_mode(FOCUS_NONE); hbc->add_child(scroll[i]); values[i] = memnew(SpinBox); scroll[i]->share(values[i]); hbc->add_child(values[i]); + values[i]->get_line_edit()->connect("focus_entered", this, "_focus_enter"); + values[i]->get_line_edit()->connect("focus_exited", this, "_focus_exit"); scroll[i]->set_min(0); scroll[i]->set_page(0); @@ -589,6 +627,9 @@ ColorPicker::ColorPicker() : c_text = memnew(LineEdit); hhb->add_child(c_text); c_text->connect("text_entered", this, "_html_entered"); + c_text->connect("focus_entered", this, "_focus_enter"); + c_text->connect("focus_exited", this, "_html_focus_exit"); + text_type->set_text("#"); c_text->set_h_size_flags(SIZE_EXPAND_FILL); diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index 01ae1cc464..40ded4fff5 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -88,6 +88,9 @@ private: void _screen_input(const Ref<InputEvent> &p_event); void _add_preset_pressed(); void _screen_pick_pressed(); + void _focus_enter(); + void _focus_exit(); + void _html_focus_exit(); protected: void _notification(int); diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp index c2b8a7dfab..9948672097 100644 --- a/scene/gui/grid_container.cpp +++ b/scene/gui/grid_container.cpp @@ -36,10 +36,10 @@ void GridContainer::_notification(int p_what) { case NOTIFICATION_SORT_CHILDREN: { - Map<int, int> col_minw; - Map<int, int> row_minh; - Set<int> col_expanded; - Set<int> row_expanded; + Map<int, int> col_minw; // max of min_width of all controls in each col (indexed by col) + Map<int, int> row_minh; // max of min_height of all controls in each row (indexed by row) + Set<int> col_expanded; // columns which have the SIZE_EXPAND flag set + Set<int> row_expanded; // rows which have the SIZE_EXPAND flag set int hsep = get_constant("hseparation"); int vsep = get_constant("vseparation"); @@ -88,13 +88,13 @@ void GridContainer::_notification(int p_what) { remaining_space.width -= hsep * (max_col - 1); bool can_fit = false; - while (!can_fit) { + while (!can_fit && col_expanded.size() > 0) { // Check if all minwidth constraints are ok if we use the remaining space can_fit = true; - int max_index = 0; + int max_index = col_expanded.front()->get(); for (Set<int>::Element *E = col_expanded.front(); E; E = E->next()) { if (col_minw[E->get()] > col_minw[max_index]) { - max_index = col_minw[E->get()]; + max_index = E->get(); } if (can_fit && (remaining_space.width / col_expanded.size()) < col_minw[E->get()]) { can_fit = false; @@ -109,13 +109,13 @@ void GridContainer::_notification(int p_what) { } can_fit = false; - while (!can_fit) { + while (!can_fit && row_expanded.size() > 0) { // Check if all minwidth constraints are ok if we use the remaining space can_fit = true; - int max_index = 0; + int max_index = row_expanded.front()->get(); for (Set<int>::Element *E = row_expanded.front(); E; E = E->next()) { if (row_minh[E->get()] > row_minh[max_index]) { - max_index = row_minh[E->get()]; + max_index = E->get(); } if (can_fit && (remaining_space.height / row_expanded.size()) < row_minh[E->get()]) { can_fit = false; @@ -130,8 +130,8 @@ void GridContainer::_notification(int p_what) { } // Finally, fit the nodes - int col_expand = remaining_space.width / col_expanded.size(); - int row_expand = remaining_space.height / row_expanded.size(); + int col_expand = col_expanded.size() > 0 ? remaining_space.width / col_expanded.size() : 0; + int row_expand = row_expanded.size() > 0 ? remaining_space.height / row_expanded.size() : 0; int col_ofs = 0; int row_ofs = 0; diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index f7574ca2cd..cc17e6bcd8 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -295,35 +295,21 @@ int ItemList::get_current() const { return current; } -void ItemList::move_item(int p_item, int p_to_pos) { +void ItemList::move_item(int p_from_idx, int p_to_idx) { - ERR_FAIL_INDEX(p_item, items.size()); - ERR_FAIL_INDEX(p_to_pos, items.size() + 1); + ERR_FAIL_INDEX(p_from_idx, items.size()); + ERR_FAIL_INDEX(p_to_idx, items.size()); - Item it = items[p_item]; - items.remove(p_item); - - if (p_to_pos > p_item) { - p_to_pos--; + if (is_anything_selected() && get_selected_items()[0] == p_from_idx) { + current = p_to_idx; } - if (p_to_pos >= items.size()) { - items.push_back(it); - } else { - items.insert(p_to_pos, it); - } - - if (current < 0) { - //do none - } else if (p_item == current) { - current = p_to_pos; - } else if (p_to_pos > p_item && current > p_item && current < p_to_pos) { - current--; - } else if (p_to_pos < p_item && current < p_item && current > p_to_pos) { - current++; - } + Item item = items[p_from_idx]; + items.remove(p_from_idx); + items.insert(p_to_idx, item); update(); + shape_changed = true; } int ItemList::get_item_count() const { @@ -1423,9 +1409,13 @@ void ItemList::_bind_methods() { ClassDB::bind_method(D_METHOD("select", "idx", "single"), &ItemList::select, DEFVAL(true)); ClassDB::bind_method(D_METHOD("unselect", "idx"), &ItemList::unselect); + ClassDB::bind_method(D_METHOD("unselect_all"), &ItemList::unselect_all); + ClassDB::bind_method(D_METHOD("is_selected", "idx"), &ItemList::is_selected); ClassDB::bind_method(D_METHOD("get_selected_items"), &ItemList::get_selected_items); + ClassDB::bind_method(D_METHOD("move_item", "p_from_idx", "p_to_idx"), &ItemList::move_item); + ClassDB::bind_method(D_METHOD("get_item_count"), &ItemList::get_item_count); ClassDB::bind_method(D_METHOD("remove_item", "idx"), &ItemList::remove_item); @@ -1465,6 +1455,8 @@ void ItemList::_bind_methods() { ClassDB::bind_method(D_METHOD("set_auto_height", "enable"), &ItemList::set_auto_height); ClassDB::bind_method(D_METHOD("has_auto_height"), &ItemList::has_auto_height); + ClassDB::bind_method(D_METHOD("is_anything_selected"), &ItemList::is_anything_selected); + ClassDB::bind_method(D_METHOD("get_item_at_position", "position", "exact"), &ItemList::get_item_at_position, DEFVAL(false)); ClassDB::bind_method(D_METHOD("ensure_current_is_visible"), &ItemList::ensure_current_is_visible); diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h index 7f34a250bd..0fa0dd415b 100644 --- a/scene/gui/item_list.h +++ b/scene/gui/item_list.h @@ -169,7 +169,7 @@ public: void set_current(int p_current); int get_current() const; - void move_item(int p_item, int p_to_pos); + void move_item(int p_from_idx, int p_to_idx); int get_item_count() const; void remove_item(int p_idx); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 95d9173d39..48bd733e80 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -2141,9 +2141,12 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (completion_index > 0) { completion_index--; - completion_current = completion_options[completion_index]; - update(); + } else { + completion_index = completion_options.size() - 1; } + completion_current = completion_options[completion_index]; + update(); + accept_event(); return; } @@ -2152,9 +2155,12 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (completion_index < completion_options.size() - 1) { completion_index++; - completion_current = completion_options[completion_index]; - update(); + } else { + completion_index = 0; } + completion_current = completion_options[completion_index]; + update(); + accept_event(); return; } @@ -5232,7 +5238,7 @@ void TextEdit::_update_completion_candidates() { } else { - while (cofs > 0 && l[cofs - 1] > 32 && _is_completable(l[cofs - 1])) { + while (cofs > 0 && l[cofs - 1] > 32 && (l[cofs - 1] == '/' || _is_completable(l[cofs - 1]))) { s = String::chr(l[cofs - 1]) + s; if (l[cofs - 1] == '\'' || l[cofs - 1] == '"' || l[cofs - 1] == '$') break; diff --git a/scene/gui/texture_progress.cpp b/scene/gui/texture_progress.cpp index 4a419098a0..4b3ba6df3c 100644 --- a/scene/gui/texture_progress.cpp +++ b/scene/gui/texture_progress.cpp @@ -117,22 +117,45 @@ Point2 TextureProgress::unit_val_to_uv(float val) { Point2 p = get_relative_center(); - if (val < 0.125) - return Point2(p.x + (1 - p.x) * val * 8, 0); - if (val < 0.25) - return Point2(1, p.y * (val - 0.125) * 8); - if (val < 0.375) - return Point2(1, p.y + (1 - p.y) * (val - 0.25) * 8); - if (val < 0.5) - return Point2(1 - (1 - p.x) * (val - 0.375) * 8, 1); - if (val < 0.625) - return Point2(p.x * (1 - (val - 0.5) * 8), 1); - if (val < 0.75) - return Point2(0, 1 - ((1 - p.y) * (val - 0.625) * 8)); - if (val < 0.875) - return Point2(0, p.y - p.y * (val - 0.75) * 8); - else - return Point2(p.x * (val - 0.875) * 8, 0); + // Minimal version of Liang-Barsky clipping algorithm + float angle = (val * Math_TAU) - Math_PI * 0.5; + Point2 dir = Vector2(Math::cos(angle), Math::sin(angle)); + float t1 = 1.0; + float cp; + float cq; + float cr; + float edgeLeft = 0.0; + float edgeRight = 1.0; + float edgeBottom = 0.0; + float edgeTop = 1.0; + + for (int edge = 0; edge < 4; edge++) { + if (edge == 0) { + if (dir.x > 0) + continue; + cp = -dir.x; + cq = -(edgeLeft - p.x); + } else if (edge == 1) { + if (dir.x < 0) + continue; + cp = dir.x; + cq = (edgeRight - p.x); + } else if (edge == 2) { + if (dir.y > 0) + continue; + cp = -dir.y; + cq = -(edgeBottom - p.y); + } else if (edge == 3) { + if (dir.y < 0) + continue; + cp = dir.y; + cq = (edgeTop - p.y); + } + cr = cq / cp; + if (cr >= 0 && cr < t1) + t1 = cr; + } + return (p + t1 * dir); } Point2 TextureProgress::get_relative_center() { diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 136fd4cfd1..12c3da78bd 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -615,6 +615,8 @@ void SceneTree::_notification(int p_notification) { } } break; case NOTIFICATION_OS_MEMORY_WARNING: + case NOTIFICATION_WM_MOUSE_ENTER: + case NOTIFICATION_WM_MOUSE_EXIT: case NOTIFICATION_WM_FOCUS_IN: case NOTIFICATION_WM_FOCUS_OUT: { diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp index 23c6dc200b..26e29b3ccb 100644 --- a/scene/resources/dynamic_font.cpp +++ b/scene/resources/dynamic_font.cpp @@ -80,6 +80,14 @@ void DynamicFontData::set_force_autohinter(bool p_force) { void DynamicFontData::_bind_methods() { ClassDB::bind_method(D_METHOD("set_font_path", "path"), &DynamicFontData::set_font_path); ClassDB::bind_method(D_METHOD("get_font_path"), &DynamicFontData::get_font_path); + ClassDB::bind_method(D_METHOD("set_hinting", "mode"), &DynamicFontData::set_hinting); + ClassDB::bind_method(D_METHOD("get_hinting"), &DynamicFontData::get_hinting); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting"); + + BIND_ENUM_CONSTANT(HINTING_NONE); + BIND_ENUM_CONSTANT(HINTING_LIGHT); + BIND_ENUM_CONSTANT(HINTING_NORMAL); ADD_PROPERTY(PropertyInfo(Variant::STRING, "font_path", PROPERTY_HINT_FILE, "*.ttf,*.otf"), "set_font_path", "get_font_path"); } @@ -87,6 +95,7 @@ void DynamicFontData::_bind_methods() { DynamicFontData::DynamicFontData() { force_autohinter = false; + hinting = DynamicFontData::HINTING_NORMAL; font_mem = NULL; font_mem_size = 0; } @@ -212,8 +221,6 @@ Error DynamicFontAtSize::_load() { if (id.filter) texture_flags |= Texture::FLAG_FILTER; - //print_line("ASCENT: "+itos(ascent)+" descent "+itos(descent)+" hinted: "+itos(face->face_flags&FT_FACE_FLAG_HINTER)); - valid = true; return OK; } @@ -454,15 +461,28 @@ void DynamicFontAtSize::_update_char(CharType p_char) { char_map[p_char] = ch; return; } - int error = FT_Load_Char(face, p_char, FT_HAS_COLOR(face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)); + + int ft_hinting; + + switch (font->hinting) { + case DynamicFontData::HINTING_NONE: + ft_hinting = FT_LOAD_NO_HINTING; + break; + case DynamicFontData::HINTING_LIGHT: + ft_hinting = FT_LOAD_TARGET_LIGHT; + break; + default: + ft_hinting = FT_LOAD_TARGET_NORMAL; + break; + } + + int error = FT_Load_Char(face, p_char, FT_HAS_COLOR(face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting); if (!error) { error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); } if (error) { int advance = 0; - //stbtt_GetCodepointHMetrics(&font->info, p_char, &advance, 0); - //print_line("char has no bitmap: "+itos(p_char)+" but advance is "+itos(advance*scale)); Character ch; ch.texture_idx = -1; ch.advance = advance; @@ -477,7 +497,6 @@ void DynamicFontAtSize::_update_char(CharType p_char) { int w = slot->bitmap.width; int h = slot->bitmap.rows; - //int p = slot->bitmap.pitch; int yofs = slot->bitmap_top; int xofs = slot->bitmap_left; int advance = slot->advance.x >> 6; @@ -536,8 +555,6 @@ void DynamicFontAtSize::_update_char(CharType p_char) { break; } - //print_line("CHAR: "+String::chr(p_char)+" TEX INDEX: "+itos(tex_index)+" X: "+itos(tex_x)+" Y: "+itos(tex_y)); - if (tex_index == -1) { //could not find texture to fit, create one tex_x = 0; @@ -645,8 +662,6 @@ void DynamicFontAtSize::_update_char(CharType p_char) { chr.rect.position /= oversampling; chr.rect.size /= oversampling; - //print_line("CHAR: "+String::chr(p_char)+" TEX INDEX: "+itos(tex_index)+" RECT: "+chr.rect+" X OFS: "+itos(xofs)+" Y OFS: "+itos(yofs)); - char_map[p_char] = chr; } @@ -758,6 +773,18 @@ void DynamicFont::set_use_filter(bool p_enable) { _reload_cache(); } +DynamicFontData::Hinting DynamicFontData::get_hinting() const { + + return hinting; +} + +void DynamicFontData::set_hinting(Hinting p_hinting) { + + if (hinting == p_hinting) + return; + hinting = p_hinting; +} + int DynamicFont::get_spacing(int p_type) const { if (p_type == SPACING_TOP) { diff --git a/scene/resources/dynamic_font.h b/scene/resources/dynamic_font.h index d8adf35b6b..db8bd87587 100644 --- a/scene/resources/dynamic_font.h +++ b/scene/resources/dynamic_font.h @@ -64,10 +64,20 @@ public: } }; + enum Hinting { + HINTING_NONE, + HINTING_LIGHT, + HINTING_NORMAL + }; + + Hinting get_hinting() const; + void set_hinting(Hinting p_hinting); + private: const uint8_t *font_mem; int font_mem_size; bool force_autohinter; + Hinting hinting; String font_path; Map<CacheID, DynamicFontAtSize *> size_cache; @@ -91,6 +101,8 @@ public: ~DynamicFontData(); }; +VARIANT_ENUM_CAST(DynamicFontData::Hinting); + class DynamicFontAtSize : public Reference { GDCLASS(DynamicFontAtSize, Reference) diff --git a/servers/physics_2d/joints_2d_sw.cpp b/servers/physics_2d/joints_2d_sw.cpp index 7fba8acebd..d49c1b8376 100644 --- a/servers/physics_2d/joints_2d_sw.cpp +++ b/servers/physics_2d/joints_2d_sw.cpp @@ -146,14 +146,19 @@ bool PinJoint2DSW::setup(real_t p_step) { return true; } +inline Vector2 custom_cross(const Vector2 &p_vec, real_t p_other) { + + return Vector2(p_other * p_vec.y, -p_other * p_vec.x); +} + void PinJoint2DSW::solve(real_t p_step) { // compute relative velocity - Vector2 vA = A->get_linear_velocity() - rA.cross(A->get_angular_velocity()); + Vector2 vA = A->get_linear_velocity() - custom_cross(rA, A->get_angular_velocity()); Vector2 rel_vel; if (B) - rel_vel = B->get_linear_velocity() - rB.cross(B->get_angular_velocity()) - vA; + rel_vel = B->get_linear_velocity() - custom_cross(rB, B->get_angular_velocity()) - vA; else rel_vel = -vA; diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index aaac32a4f2..1bad7e652b 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -80,6 +80,7 @@ static void _debugger_get_resource_usage(List<ScriptDebuggerRemote::ResourceUsag ShaderTypes *shader_types = NULL; PhysicsServer *_createGodotPhysicsCallback() { + WARN_PRINT("The GodotPhysics 3D physics engine is deprecated and will be removed in Godot 3.2. You should use the Bullet physics engine instead (configurable in your project settings)."); return memnew(PhysicsServerSW); } @@ -163,8 +164,8 @@ void register_server_types() { GLOBAL_DEF(PhysicsServerManager::setting_property_name, "DEFAULT"); ProjectSettings::get_singleton()->set_custom_property_info(PhysicsServerManager::setting_property_name, PropertyInfo(Variant::STRING, PhysicsServerManager::setting_property_name, PROPERTY_HINT_ENUM, "DEFAULT")); - PhysicsServerManager::register_server("GodotPhysics", &_createGodotPhysicsCallback); - PhysicsServerManager::set_default_server("GodotPhysics"); + PhysicsServerManager::register_server("GodotPhysics - deprecated", &_createGodotPhysicsCallback); + PhysicsServerManager::set_default_server("GodotPhysics - deprecated"); } void unregister_server_types() { diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index 1f66b0ba9d..d5a9bfb606 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -124,6 +124,9 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "TYPE_ISAMPLER2D", "TYPE_USAMPLER2D", "TYPE_SAMPLERCUBE", + "INTERPOLATION_FLAT", + "INTERPOLATION_NO_PERSPECTIVE", + "INTERPOLATION_SMOOTH", "PRECISION_LOW", "PRECISION_MID", "PRECISION_HIGH", @@ -1608,7 +1611,7 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = { { "mix", TYPE_VEC3, { TYPE_VEC3, TYPE_VEC3, TYPE_VEC3, TYPE_VOID } }, { "mix", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_FLOAT, TYPE_VOID } }, { "mix", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_BOOL, TYPE_VOID } }, - { "mix", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_BVEC3, TYPE_VOID } }, + { "mix", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_BVEC4, TYPE_VOID } }, { "mix", TYPE_VEC4, { TYPE_VEC4, TYPE_VEC4, TYPE_VEC4, TYPE_VOID } }, { "step", TYPE_FLOAT, { TYPE_FLOAT, TYPE_FLOAT, TYPE_VOID } }, @@ -3699,26 +3702,6 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct //todo parse default value tk = _get_token(); - if (tk.type == TK_OP_ASSIGN) { - - Node *expr = _parse_and_reduce_expression(NULL, Map<StringName, BuiltInInfo>()); - if (!expr) - return ERR_PARSE_ERROR; - if (expr->type != Node::TYPE_CONSTANT) { - _set_error("Expected constant expression after '='"); - return ERR_PARSE_ERROR; - } - - ConstantNode *cn = static_cast<ConstantNode *>(expr); - - uniform.default_value.resize(cn->values.size()); - - if (!convert_constant(cn, uniform.type, uniform.default_value.ptrw())) { - _set_error("Can't convert constant to " + get_datatype_name(uniform.type)); - return ERR_PARSE_ERROR; - } - tk = _get_token(); - } if (tk.type == TK_COLON) { //hint @@ -3834,6 +3817,27 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct tk = _get_token(); } + if (tk.type == TK_OP_ASSIGN) { + + Node *expr = _parse_and_reduce_expression(NULL, Map<StringName, BuiltInInfo>()); + if (!expr) + return ERR_PARSE_ERROR; + if (expr->type != Node::TYPE_CONSTANT) { + _set_error("Expected constant expression after '='"); + return ERR_PARSE_ERROR; + } + + ConstantNode *cn = static_cast<ConstantNode *>(expr); + + uniform.default_value.resize(cn->values.size()); + + if (!convert_constant(cn, uniform.type, uniform.default_value.ptrw())) { + _set_error("Can't convert constant to " + get_datatype_name(uniform.type)); + return ERR_PARSE_ERROR; + } + tk = _get_token(); + } + shader->uniforms[name] = uniform; if (tk.type != TK_SEMICOLON) { diff --git a/servers/visual/visual_server_raster.cpp b/servers/visual/visual_server_raster.cpp index 64a3502e40..fca3126604 100644 --- a/servers/visual/visual_server_raster.cpp +++ b/servers/visual/visual_server_raster.cpp @@ -205,4 +205,5 @@ VisualServerRaster::~VisualServerRaster() { memdelete(VSG::canvas); memdelete(VSG::viewport); memdelete(VSG::rasterizer); + memdelete(VSG::scene); } diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp index 0920fa748b..884489375f 100644 --- a/servers/visual/visual_server_scene.cpp +++ b/servers/visual/visual_server_scene.cpp @@ -3333,6 +3333,7 @@ VisualServerScene::~VisualServerScene() { #ifndef NO_THREADS probe_bake_thread_exit = true; + probe_bake_sem->post(); Thread::wait_to_finish(probe_bake_thread); memdelete(probe_bake_thread); memdelete(probe_bake_sem); diff --git a/thirdparty/libsimplewebm/libwebm/README.libvpx b/thirdparty/libsimplewebm/libwebm/README.libvpx index ae62f36525..1aa93b75aa 100644 --- a/thirdparty/libsimplewebm/libwebm/README.libvpx +++ b/thirdparty/libsimplewebm/libwebm/README.libvpx @@ -1,5 +1,5 @@ URL: https://chromium.googlesource.com/webm/libwebm -Version: 32d5ac49414a8914ec1e1f285f3f927c6e8ec29d +Version: d7c62173ff6b4a5e0a2f86683a5b67db98cf09bf License: BSD License File: LICENSE.txt diff --git a/thirdparty/libsimplewebm/libwebm/common/webmids.h b/thirdparty/libsimplewebm/libwebm/common/webmids.h index 32a0c5fb91..89d722a71b 100644 --- a/thirdparty/libsimplewebm/libwebm/common/webmids.h +++ b/thirdparty/libsimplewebm/libwebm/common/webmids.h @@ -124,6 +124,14 @@ enum MkvId { kMkvLuminanceMin = 0x55DA, // end mastering metadata // end colour + // projection + kMkvProjection = 0x7670, + kMkvProjectionType = 0x7671, + kMkvProjectionPrivate = 0x7672, + kMkvProjectionPoseYaw = 0x7673, + kMkvProjectionPosePitch = 0x7674, + kMkvProjectionPoseRoll = 0x7675, + // end projection // audio kMkvAudio = 0xE1, kMkvSamplingFrequency = 0xB5, diff --git a/thirdparty/libsimplewebm/libwebm/mkvparser/mkvparser.cc b/thirdparty/libsimplewebm/libwebm/mkvparser/mkvparser.cc index d2375edb3b..e7b76f7da1 100644 --- a/thirdparty/libsimplewebm/libwebm/mkvparser/mkvparser.cc +++ b/thirdparty/libsimplewebm/libwebm/mkvparser/mkvparser.cc @@ -12,51 +12,30 @@ #define MSC_COMPAT #endif -#include <assert.h> -#include <float.h> -#include <limits.h> -#include <math.h> -#include <string.h> +#include <cassert> +#include <cfloat> +#include <climits> +#include <cmath> +#include <cstring> +#include <memory> +#include <new> #include "common/webmids.h" namespace mkvparser { +const long long kStringElementSizeLimit = 20 * 1000 * 1000; const float MasteringMetadata::kValueNotPresent = FLT_MAX; const long long Colour::kValueNotPresent = LLONG_MAX; +const float Projection::kValueNotPresent = FLT_MAX; #ifdef MSC_COMPAT inline bool isnan(double val) { return !!_isnan(val); } inline bool isinf(double val) { return !_finite(val); } +#else +inline bool isnan(double val) { return std::isnan(val); } +inline bool isinf(double val) { return std::isinf(val); } #endif // MSC_COMPAT -template<typename T> -class my_auto_ptr { - my_auto_ptr(const my_auto_ptr &); - T *operator =(const my_auto_ptr &); - - T *m_ptr; -public: - my_auto_ptr(T *ptr) : - m_ptr(ptr) - {} - my_auto_ptr() : - m_ptr(NULL) - {} - ~my_auto_ptr() { - delete m_ptr; - } - - T *release() { - T *ptr = m_ptr; - m_ptr = NULL; - return ptr; - } - - T *operator ->() const { - return m_ptr; - } -}; - IMkvReader::~IMkvReader() {} template <typename Type> @@ -72,7 +51,7 @@ Type* SafeArrayAlloc(unsigned long long num_elements, if (num_bytes != static_cast<size_t>(num_bytes)) return NULL; - return new Type[static_cast<size_t>(num_bytes)]; + return new (std::nothrow) Type[static_cast<size_t>(num_bytes)]; } void GetVersion(int& major, int& minor, int& build, int& revision) { @@ -304,7 +283,7 @@ long UnserializeFloat(IMkvReader* pReader, long long pos, long long size_, result = d; } - if (isinf(result) || isnan(result)) + if (mkvparser::isinf(result) || mkvparser::isnan(result)) return E_FILE_FORMAT_INVALID; return 0; @@ -347,7 +326,7 @@ long UnserializeString(IMkvReader* pReader, long long pos, long long size, delete[] str; str = NULL; - if (size >= LONG_MAX || size < 0) + if (size >= LONG_MAX || size < 0 || size > kStringElementSizeLimit) return E_FILE_FORMAT_INVALID; // +1 for '\0' terminator @@ -818,7 +797,9 @@ long long Segment::CreateInstance(IMkvReader* pReader, long long pos, else if ((pos + size) > total) size = -1; - pSegment = new Segment(pReader, idpos, pos, size); + pSegment = new (std::nothrow) Segment(pReader, idpos, pos, size); + if (pSegment == NULL) + return E_PARSE_FAILED; return 0; // success } @@ -954,7 +935,11 @@ long long Segment::ParseHeaders() { if (m_pInfo) return E_FILE_FORMAT_INVALID; - m_pInfo = new SegmentInfo(this, pos, size, element_start, element_size); + m_pInfo = new (std::nothrow) + SegmentInfo(this, pos, size, element_start, element_size); + + if (m_pInfo == NULL) + return -1; const long status = m_pInfo->Parse(); @@ -964,7 +949,11 @@ long long Segment::ParseHeaders() { if (m_pTracks) return E_FILE_FORMAT_INVALID; - m_pTracks = new Tracks(this, pos, size, element_start, element_size); + m_pTracks = new (std::nothrow) + Tracks(this, pos, size, element_start, element_size); + + if (m_pTracks == NULL) + return -1; const long status = m_pTracks->Parse(); @@ -972,11 +961,19 @@ long long Segment::ParseHeaders() { return status; } else if (id == libwebm::kMkvCues) { if (m_pCues == NULL) { - m_pCues = new Cues(this, pos, size, element_start, element_size); + m_pCues = new (std::nothrow) + Cues(this, pos, size, element_start, element_size); + + if (m_pCues == NULL) + return -1; } } else if (id == libwebm::kMkvSeekHead) { if (m_pSeekHead == NULL) { - m_pSeekHead = new SeekHead(this, pos, size, element_start, element_size); + m_pSeekHead = new (std::nothrow) + SeekHead(this, pos, size, element_start, element_size); + + if (m_pSeekHead == NULL) + return -1; const long status = m_pSeekHead->Parse(); @@ -985,7 +982,11 @@ long long Segment::ParseHeaders() { } } else if (id == libwebm::kMkvChapters) { if (m_pChapters == NULL) { - m_pChapters = new Chapters(this, pos, size, element_start, element_size); + m_pChapters = new (std::nothrow) + Chapters(this, pos, size, element_start, element_size); + + if (m_pChapters == NULL) + return -1; const long status = m_pChapters->Parse(); @@ -994,7 +995,11 @@ long long Segment::ParseHeaders() { } } else if (id == libwebm::kMkvTags) { if (m_pTags == NULL) { - m_pTags = new Tags(this, pos, size, element_start, element_size); + m_pTags = new (std::nothrow) + Tags(this, pos, size, element_start, element_size); + + if (m_pTags == NULL) + return -1; const long status = m_pTags->Parse(); @@ -1136,7 +1141,9 @@ long Segment::DoLoadCluster(long long& pos, long& len) { if (m_pCues == NULL) { const long long element_size = (pos - idpos) + size; - m_pCues = new Cues(this, pos, size, idpos, element_size); + m_pCues = new (std::nothrow) Cues(this, pos, size, idpos, element_size); + if (m_pCues == NULL) + return -1; } m_pos = pos + size; // consume payload @@ -1284,9 +1291,7 @@ long Segment::DoLoadCluster(long long& pos, long& len) { pos += cluster_size; m_pos = pos; - // -- GODOT start -- - delete pCluster; - // -- GODOT end -- + if (segment_stop > 0 && m_pos > segment_stop) return E_FILE_FORMAT_INVALID; @@ -1344,7 +1349,9 @@ bool Segment::AppendCluster(Cluster* pCluster) { if (count >= size) { const long n = (size <= 0) ? 2048 : 2 * size; - Cluster** const qq = new Cluster*[n]; + Cluster** const qq = new (std::nothrow) Cluster*[n]; + if (qq == NULL) + return false; Cluster** q = qq; Cluster** p = m_clusters; @@ -1399,7 +1406,9 @@ bool Segment::PreloadCluster(Cluster* pCluster, ptrdiff_t idx) { if (count >= size) { const long n = (size <= 0) ? 2048 : 2 * size; - Cluster** const qq = new Cluster*[n]; + Cluster** const qq = new (std::nothrow) Cluster*[n]; + if (qq == NULL) + return false; Cluster** q = qq; Cluster** p = m_clusters; @@ -1468,6 +1477,8 @@ long Segment::Load() { } } +SeekHead::Entry::Entry() : id(0), pos(0), element_start(0), element_size(0) {} + SeekHead::SeekHead(Segment* pSegment, long long start, long long size_, long long element_start, long long element_size) : m_pSegment(pSegment), @@ -1518,9 +1529,19 @@ long SeekHead::Parse() { if (pos != stop) return E_FILE_FORMAT_INVALID; - m_entries = new Entry[entry_count]; + if (entry_count > 0) { + m_entries = new (std::nothrow) Entry[entry_count]; - m_void_elements = new VoidElement[void_element_count]; + if (m_entries == NULL) + return -1; + } + + if (void_element_count > 0) { + m_void_elements = new (std::nothrow) VoidElement[void_element_count]; + + if (m_void_elements == NULL) + return -1; + } // now parse the entries and void elements @@ -1539,14 +1560,14 @@ long SeekHead::Parse() { if (status < 0) // error return status; - if (id == libwebm::kMkvSeek) { + if (id == libwebm::kMkvSeek && entry_count > 0) { if (ParseEntry(pReader, pos, size, pEntry)) { Entry& e = *pEntry++; e.element_start = idpos; e.element_size = (pos + size) - idpos; } - } else if (id == libwebm::kMkvVoid) { + } else if (id == libwebm::kMkvVoid && void_element_count > 0) { VoidElement& e = *pVoidElement++; e.element_start = idpos; @@ -1708,7 +1729,10 @@ long Segment::ParseCues(long long off, long long& pos, long& len) { const long long element_size = element_stop - element_start; - m_pCues = new Cues(this, pos, size, element_start, element_size); + m_pCues = + new (std::nothrow) Cues(this, pos, size, element_start, element_size); + if (m_pCues == NULL) + return -1; return 0; // success } @@ -1750,18 +1774,7 @@ bool SeekHead::ParseEntry(IMkvReader* pReader, long long start, long long size_, if ((pos + seekIdSize) > stop) return false; - // Note that the SeekId payload really is serialized - // as a "Matroska integer", not as a plain binary value. - // In fact, Matroska requires that ID values in the - // stream exactly match the binary representation as listed - // in the Matroska specification. - // - // This parser is more liberal, and permits IDs to have - // any width. (This could make the representation in the stream - // different from what's in the spec, but it doesn't matter here, - // since we always normalize "Matroska integer" values.) - - pEntry->id = ReadUInt(pReader, pos, len); // payload + pEntry->id = ReadID(pReader, pos, len); // payload if (pEntry->id <= 0) return false; @@ -1900,7 +1913,9 @@ bool Cues::PreloadCuePoint(long& cue_points_size, long long pos) const { if (m_preload_count >= cue_points_size) { const long n = (cue_points_size <= 0) ? 2048 : 2 * cue_points_size; - CuePoint** const qq = new CuePoint*[n]; + CuePoint** const qq = new (std::nothrow) CuePoint*[n]; + if (qq == NULL) + return false; CuePoint** q = qq; // beginning of target @@ -1916,7 +1931,9 @@ bool Cues::PreloadCuePoint(long& cue_points_size, long long pos) const { cue_points_size = n; } - CuePoint* const pCP = new CuePoint(m_preload_count, pos); + CuePoint* const pCP = new (std::nothrow) CuePoint(m_preload_count, pos); + if (pCP == NULL) + return false; m_cue_points[m_preload_count++] = pCP; return true; @@ -2322,7 +2339,9 @@ bool CuePoint::Load(IMkvReader* pReader) { // << " timecode=" << m_timecode // << endl; - m_track_positions = new TrackPosition[m_track_positions_count]; + m_track_positions = new (std::nothrow) TrackPosition[m_track_positions_count]; + if (m_track_positions == NULL) + return false; // Now parse track positions @@ -2412,7 +2431,9 @@ bool CuePoint::TrackPosition::Parse(IMkvReader* pReader, long long start_, } const CuePoint::TrackPosition* CuePoint::Find(const Track* pTrack) const { - assert(pTrack); + if (pTrack == NULL) { + return NULL; + } const long long n = pTrack->GetNumber(); @@ -2917,7 +2938,10 @@ long Segment::DoParseNext(const Cluster*& pResult, long long& pos, long& len) { const long long element_size = element_stop - element_start; if (m_pCues == NULL) { - m_pCues = new Cues(this, pos, size, element_start, element_size); + m_pCues = new (std::nothrow) + Cues(this, pos, size, element_start, element_size); + if (m_pCues == NULL) + return false; } pos += size; // consume payload @@ -3268,7 +3292,10 @@ bool Chapters::ExpandEditionsArray() { const int size = (m_editions_size == 0) ? 1 : 2 * m_editions_size; - Edition* const editions = new Edition[size]; + Edition* const editions = new (std::nothrow) Edition[size]; + + if (editions == NULL) + return false; for (int idx = 0; idx < m_editions_count; ++idx) { m_editions[idx].ShallowCopy(editions[idx]); @@ -3380,7 +3407,10 @@ bool Chapters::Edition::ExpandAtomsArray() { const int size = (m_atoms_size == 0) ? 1 : 2 * m_atoms_size; - Atom* const atoms = new Atom[size]; + Atom* const atoms = new (std::nothrow) Atom[size]; + + if (atoms == NULL) + return false; for (int idx = 0; idx < m_atoms_count; ++idx) { m_atoms[idx].ShallowCopy(atoms[idx]); @@ -3565,7 +3595,10 @@ bool Chapters::Atom::ExpandDisplaysArray() { const int size = (m_displays_size == 0) ? 1 : 2 * m_displays_size; - Display* const displays = new Display[size]; + Display* const displays = new (std::nothrow) Display[size]; + + if (displays == NULL) + return false; for (int idx = 0; idx < m_displays_count; ++idx) { m_displays[idx].ShallowCopy(displays[idx]); @@ -3725,7 +3758,10 @@ bool Tags::ExpandTagsArray() { const int size = (m_tags_size == 0) ? 1 : 2 * m_tags_size; - Tag* const tags = new Tag[size]; + Tag* const tags = new (std::nothrow) Tag[size]; + + if (tags == NULL) + return false; for (int idx = 0; idx < m_tags_count; ++idx) { m_tags[idx].ShallowCopy(tags[idx]); @@ -3836,7 +3872,10 @@ bool Tags::Tag::ExpandSimpleTagsArray() { const int size = (m_simple_tags_size == 0) ? 1 : 2 * m_simple_tags_size; - SimpleTag* const displays = new SimpleTag[size]; + SimpleTag* const displays = new (std::nothrow) SimpleTag[size]; + + if (displays == NULL) + return false; for (int idx = 0; idx < m_simple_tags_count; ++idx) { m_simple_tags[idx].ShallowCopy(displays[idx]); @@ -3994,7 +4033,7 @@ long SegmentInfo::Parse() { } const double rollover_check = m_duration * m_timecodeScale; - if (rollover_check > LLONG_MAX) + if (rollover_check > static_cast<double>(LLONG_MAX)) return E_FILE_FORMAT_INVALID; if (pos != stop) @@ -4085,7 +4124,7 @@ ContentEncoding::~ContentEncoding() { } const ContentEncoding::ContentCompression* - ContentEncoding::GetCompressionByIndex(unsigned long idx) const { +ContentEncoding::GetCompressionByIndex(unsigned long idx) const { const ptrdiff_t count = compression_entries_end_ - compression_entries_; assert(count >= 0); @@ -4181,12 +4220,20 @@ long ContentEncoding::ParseContentEncodingEntry(long long start, long long size, return -1; if (compression_count > 0) { - compression_entries_ = new ContentCompression*[compression_count]; + compression_entries_ = + new (std::nothrow) ContentCompression*[compression_count]; + if (!compression_entries_) + return -1; compression_entries_end_ = compression_entries_; } if (encryption_count > 0) { - encryption_entries_ = new ContentEncryption*[encryption_count]; + encryption_entries_ = + new (std::nothrow) ContentEncryption*[encryption_count]; + if (!encryption_entries_) { + delete[] compression_entries_; + return -1; + } encryption_entries_end_ = encryption_entries_; } @@ -4206,7 +4253,10 @@ long ContentEncoding::ParseContentEncodingEntry(long long start, long long size, } else if (id == libwebm::kMkvContentEncodingType) { encoding_type_ = UnserializeUInt(pReader, pos, size); } else if (id == libwebm::kMkvContentCompression) { - ContentCompression* const compression = new ContentCompression(); + ContentCompression* const compression = + new (std::nothrow) ContentCompression(); + if (!compression) + return -1; status = ParseCompressionEntry(pos, size, pReader, compression); if (status) { @@ -4215,7 +4265,10 @@ long ContentEncoding::ParseContentEncodingEntry(long long start, long long size, } *compression_entries_end_++ = compression; } else if (id == libwebm::kMkvContentEncryption) { - ContentEncryption* const encryption = new ContentEncryption(); + ContentEncryption* const encryption = + new (std::nothrow) ContentEncryption(); + if (!encryption) + return -1; status = ParseEncryptionEntry(pos, size, pReader, encryption); if (status) { @@ -4421,7 +4474,11 @@ long Track::Create(Segment* pSegment, const Info& info, long long element_start, if (pResult) return -1; - Track* const pTrack = new Track(pSegment, element_start, element_size); + Track* const pTrack = + new (std::nothrow) Track(pSegment, element_start, element_size); + + if (pTrack == NULL) + return -1; // generic error const int status = info.Copy(pTrack->m_info); @@ -4873,7 +4930,10 @@ long Track::ParseContentEncodingsEntry(long long start, long long size) { if (count <= 0) return -1; - content_encoding_entries_ = new ContentEncoding*[count]; + content_encoding_entries_ = new (std::nothrow) ContentEncoding*[count]; + if (!content_encoding_entries_) + return -1; + content_encoding_entries_end_ = content_encoding_entries_; pos = start; @@ -4885,7 +4945,10 @@ long Track::ParseContentEncodingsEntry(long long start, long long size) { // pos now designates start of element if (id == libwebm::kMkvContentEncoding) { - ContentEncoding* const content_encoding = new ContentEncoding(); + ContentEncoding* const content_encoding = + new (std::nothrow) ContentEncoding(); + if (!content_encoding) + return -1; status = content_encoding->ParseContentEncodingEntry(pos, size, pReader); if (status) { @@ -4919,20 +4982,27 @@ bool PrimaryChromaticity::Parse(IMkvReader* reader, long long read_pos, if (!reader) return false; - my_auto_ptr<PrimaryChromaticity> chromaticity_ptr(*chromaticity ? *chromaticity : new PrimaryChromaticity()); + if (!*chromaticity) + *chromaticity = new PrimaryChromaticity(); + + if (!*chromaticity) + return false; - float* value = is_x ? &chromaticity_ptr->x : &chromaticity_ptr->y; + PrimaryChromaticity* pc = *chromaticity; + float* value = is_x ? &pc->x : &pc->y; double parser_value = 0; - const long long value_parse_status = + const long long parse_status = UnserializeFloat(reader, read_pos, value_size, parser_value); - *value = static_cast<float>(parser_value); - - if (value_parse_status < 0 || *value < 0.0 || *value > 1.0) + // Valid range is [0, 1]. Make sure the double is representable as a float + // before casting. + if (parse_status < 0 || parser_value < 0.0 || parser_value > 1.0 || + (parser_value > 0.0 && parser_value < FLT_MIN)) return false; - *chromaticity = chromaticity_ptr.release(); + *value = static_cast<float>(parser_value); + return true; } @@ -4941,7 +5011,9 @@ bool MasteringMetadata::Parse(IMkvReader* reader, long long mm_start, if (!reader || *mm) return false; - my_auto_ptr<MasteringMetadata> mm_ptr(new MasteringMetadata()); + std::unique_ptr<MasteringMetadata> mm_ptr(new MasteringMetadata()); + if (!mm_ptr.get()) + return false; const long long mm_end = mm_start + mm_size; long long read_pos = mm_start; @@ -4959,6 +5031,10 @@ bool MasteringMetadata::Parse(IMkvReader* reader, long long mm_start, double value = 0; const long long value_parse_status = UnserializeFloat(reader, read_pos, child_size, value); + if (value < -FLT_MAX || value > FLT_MAX || + (value > 0.0 && value < FLT_MIN)) { + return false; + } mm_ptr->luminance_max = static_cast<float>(value); if (value_parse_status < 0 || mm_ptr->luminance_max < 0.0 || mm_ptr->luminance_max > 9999.99) { @@ -4968,6 +5044,10 @@ bool MasteringMetadata::Parse(IMkvReader* reader, long long mm_start, double value = 0; const long long value_parse_status = UnserializeFloat(reader, read_pos, child_size, value); + if (value < -FLT_MAX || value > FLT_MAX || + (value > 0.0 && value < FLT_MIN)) { + return false; + } mm_ptr->luminance_min = static_cast<float>(value); if (value_parse_status < 0 || mm_ptr->luminance_min < 0.0 || mm_ptr->luminance_min > 999.9999) { @@ -5020,7 +5100,9 @@ bool Colour::Parse(IMkvReader* reader, long long colour_start, if (!reader || *colour) return false; - my_auto_ptr<Colour> colour_ptr(new Colour()); + std::unique_ptr<Colour> colour_ptr(new Colour()); + if (!colour_ptr.get()) + return false; const long long colour_end = colour_start + colour_size; long long read_pos = colour_start; @@ -5111,11 +5193,94 @@ bool Colour::Parse(IMkvReader* reader, long long colour_start, return true; } +bool Projection::Parse(IMkvReader* reader, long long start, long long size, + Projection** projection) { + if (!reader || *projection) + return false; + + std::unique_ptr<Projection> projection_ptr(new Projection()); + if (!projection_ptr.get()) + return false; + + const long long end = start + size; + long long read_pos = start; + + while (read_pos < end) { + long long child_id = 0; + long long child_size = 0; + + const long long status = + ParseElementHeader(reader, read_pos, end, child_id, child_size); + if (status < 0) + return false; + + if (child_id == libwebm::kMkvProjectionType) { + long long projection_type = kTypeNotPresent; + projection_type = UnserializeUInt(reader, read_pos, child_size); + if (projection_type < 0) + return false; + + projection_ptr->type = static_cast<ProjectionType>(projection_type); + } else if (child_id == libwebm::kMkvProjectionPrivate) { + unsigned char* data = SafeArrayAlloc<unsigned char>(1, child_size); + + if (data == NULL) + return false; + + const int status = + reader->Read(read_pos, static_cast<long>(child_size), data); + + if (status) { + delete[] data; + return false; + } + + projection_ptr->private_data = data; + projection_ptr->private_data_length = static_cast<size_t>(child_size); + } else { + double value = 0; + const long long value_parse_status = + UnserializeFloat(reader, read_pos, child_size, value); + // Make sure value is representable as a float before casting. + if (value_parse_status < 0 || value < -FLT_MAX || value > FLT_MAX || + (value > 0.0 && value < FLT_MIN)) { + return false; + } + + switch (child_id) { + case libwebm::kMkvProjectionPoseYaw: + projection_ptr->pose_yaw = static_cast<float>(value); + break; + case libwebm::kMkvProjectionPosePitch: + projection_ptr->pose_pitch = static_cast<float>(value); + break; + case libwebm::kMkvProjectionPoseRoll: + projection_ptr->pose_roll = static_cast<float>(value); + break; + default: + return false; + } + } + + read_pos += child_size; + if (read_pos > end) + return false; + } + + *projection = projection_ptr.release(); + return true; +} + VideoTrack::VideoTrack(Segment* pSegment, long long element_start, long long element_size) - : Track(pSegment, element_start, element_size), m_colour(NULL) {} + : Track(pSegment, element_start, element_size), + m_colour(NULL), + m_projection(NULL) {} -VideoTrack::~VideoTrack() { delete m_colour; } +VideoTrack::~VideoTrack() { + delete m_colour; + delete m_projection; +} long VideoTrack::Parse(Segment* pSegment, const Info& info, long long element_start, long long element_size, @@ -5147,6 +5312,7 @@ long VideoTrack::Parse(Segment* pSegment, const Info& info, const long long stop = pos + s.size; Colour* colour = NULL; + Projection* projection = NULL; while (pos < stop) { long long id, size; @@ -5197,6 +5363,9 @@ long VideoTrack::Parse(Segment* pSegment, const Info& info, } else if (id == libwebm::kMkvColour) { if (!Colour::Parse(pReader, pos, size, &colour)) return E_FILE_FORMAT_INVALID; + } else if (id == libwebm::kMkvProjection) { + if (!Projection::Parse(pReader, pos, size, &projection)) + return E_FILE_FORMAT_INVALID; } pos += size; // consume payload @@ -5207,7 +5376,11 @@ long VideoTrack::Parse(Segment* pSegment, const Info& info, if (pos != stop) return E_FILE_FORMAT_INVALID; - VideoTrack* const pTrack = new VideoTrack(pSegment, element_start, element_size); + VideoTrack* const pTrack = + new (std::nothrow) VideoTrack(pSegment, element_start, element_size); + + if (pTrack == NULL) + return -1; // generic error const int status = info.Copy(pTrack->m_info); @@ -5224,6 +5397,7 @@ long VideoTrack::Parse(Segment* pSegment, const Info& info, pTrack->m_stereo_mode = stereo_mode; pTrack->m_rate = rate; pTrack->m_colour = colour; + pTrack->m_projection = projection; pResult = pTrack; return 0; // success @@ -5324,6 +5498,8 @@ long VideoTrack::Seek(long long time_ns, const BlockEntry*& pResult) const { Colour* VideoTrack::GetColour() const { return m_colour; } +Projection* VideoTrack::GetProjection() const { return m_projection; } + long long VideoTrack::GetWidth() const { return m_width; } long long VideoTrack::GetHeight() const { return m_height; } @@ -5406,7 +5582,11 @@ long AudioTrack::Parse(Segment* pSegment, const Info& info, if (pos != stop) return E_FILE_FORMAT_INVALID; - AudioTrack* const pTrack = new AudioTrack(pSegment, element_start, element_size); + AudioTrack* const pTrack = + new (std::nothrow) AudioTrack(pSegment, element_start, element_size); + + if (pTrack == NULL) + return -1; // generic error const int status = info.Copy(pTrack->m_info); @@ -5474,7 +5654,11 @@ long Tracks::Parse() { if (count <= 0) return 0; // success - m_trackEntries = new Track*[count]; + m_trackEntries = new (std::nothrow) Track*[count]; + + if (m_trackEntries == NULL) + return -1; + m_trackEntriesEnd = m_trackEntries; pos = m_start; @@ -6577,7 +6761,10 @@ Cluster* Cluster::Create(Segment* pSegment, long idx, long long off) { const long long element_start = pSegment->m_start + off; - return new Cluster(pSegment, idx, element_start); + Cluster* const pCluster = + new (std::nothrow) Cluster(pSegment, idx, element_start); + + return pCluster; } Cluster::Cluster() @@ -6606,8 +6793,10 @@ Cluster::Cluster(Segment* pSegment, long idx, long long element_start {} Cluster::~Cluster() { - if (m_entries_count <= 0) + if (m_entries_count <= 0) { + delete[] m_entries; return; + } BlockEntry** i = m_entries; BlockEntry** const j = m_entries + m_entries_count; @@ -6920,7 +7109,9 @@ long Cluster::CreateBlock(long long id, assert(m_entries_size == 0); m_entries_size = 1024; - m_entries = new BlockEntry*[m_entries_size]; + m_entries = new (std::nothrow) BlockEntry*[m_entries_size]; + if (m_entries == NULL) + return -1; m_entries_count = 0; } else { @@ -6931,7 +7122,9 @@ long Cluster::CreateBlock(long long id, if (m_entries_count >= m_entries_size) { const long entries_size = 2 * m_entries_size; - BlockEntry** const entries = new BlockEntry*[entries_size]; + BlockEntry** const entries = new (std::nothrow) BlockEntry*[entries_size]; + if (entries == NULL) + return -1; BlockEntry** src = m_entries; BlockEntry** const src_end = src + m_entries_count; @@ -7040,7 +7233,11 @@ long Cluster::CreateBlockGroup(long long start_offset, long long size, BlockEntry** const ppEntry = m_entries + idx; BlockEntry*& pEntry = *ppEntry; - pEntry = new BlockGroup(this, idx, bpos, bsize, prev, next, duration, discard_padding); + pEntry = new (std::nothrow) + BlockGroup(this, idx, bpos, bsize, prev, next, duration, discard_padding); + + if (pEntry == NULL) + return -1; // generic error BlockGroup* const p = static_cast<BlockGroup*>(pEntry); @@ -7068,7 +7265,10 @@ long Cluster::CreateSimpleBlock(long long st, long long sz) { BlockEntry** const ppEntry = m_entries + idx; BlockEntry*& pEntry = *ppEntry; - pEntry = new SimpleBlock(this, idx, st, sz); + pEntry = new (std::nothrow) SimpleBlock(this, idx, st, sz); + + if (pEntry == NULL) + return -1; // generic error SimpleBlock* const p = static_cast<SimpleBlock*>(pEntry); @@ -7463,7 +7663,9 @@ long Block::Parse(const Cluster* pCluster) { return E_FILE_FORMAT_INVALID; m_frame_count = 1; - m_frames = new Frame[m_frame_count]; + m_frames = new (std::nothrow) Frame[m_frame_count]; + if (m_frames == NULL) + return -1; Frame& f = m_frames[0]; f.pos = pos; @@ -7494,7 +7696,9 @@ long Block::Parse(const Cluster* pCluster) { m_frame_count = int(biased_count) + 1; - m_frames = new Frame[m_frame_count]; + m_frames = new (std::nothrow) Frame[m_frame_count]; + if (m_frames == NULL) + return -1; if (!m_frames) return E_FILE_FORMAT_INVALID; @@ -7703,6 +7907,10 @@ long Block::Parse(const Cluster* pCluster) { return E_FILE_FORMAT_INVALID; curr.len = static_cast<long>(frame_size); + // Check if size + curr.len could overflow. + if (size > LLONG_MAX - curr.len) { + return E_FILE_FORMAT_INVALID; + } size += curr.len; // contribution of this frame --frame_count; @@ -7743,7 +7951,6 @@ long Block::Parse(const Cluster* pCluster) { pf = m_frames; while (pf != pf_end) { Frame& f = *pf++; - assert((pos + f.len) <= stop); if ((pos + f.len) > stop) return E_FILE_FORMAT_INVALID; @@ -7765,6 +7972,11 @@ long long Block::GetTimeCode(const Cluster* pCluster) const { const long long tc0 = pCluster->GetTimeCode(); assert(tc0 >= 0); + // Check if tc0 + m_timecode would overflow. + if (tc0 < 0 || LLONG_MAX - tc0 < m_timecode) { + return -1; + } + const long long tc = tc0 + m_timecode; return tc; // unscaled timecode units @@ -7782,6 +7994,10 @@ long long Block::GetTime(const Cluster* pCluster) const { const long long scale = pInfo->GetTimeCodeScale(); assert(scale >= 1); + // Check if tc * scale could overflow. + if (tc != 0 && scale > LLONG_MAX / tc) { + return -1; + } const long long ns = tc * scale; return ns; diff --git a/thirdparty/libsimplewebm/libwebm/mkvparser/mkvparser.h b/thirdparty/libsimplewebm/libwebm/mkvparser/mkvparser.h index 1ef274b162..6dce7e50ba 100644 --- a/thirdparty/libsimplewebm/libwebm/mkvparser/mkvparser.h +++ b/thirdparty/libsimplewebm/libwebm/mkvparser/mkvparser.h @@ -8,7 +8,7 @@ #ifndef MKVPARSER_MKVPARSER_H_ #define MKVPARSER_MKVPARSER_H_ -#include <stddef.h> +#include <cstddef> namespace mkvparser { @@ -21,6 +21,7 @@ class IMkvReader { virtual int Read(long long pos, long len, unsigned char* buf) = 0; virtual int Length(long long* total, long long* available) = 0; + public: virtual ~IMkvReader(); }; @@ -472,6 +473,34 @@ struct Colour { MasteringMetadata* mastering_metadata; }; +struct Projection { + enum ProjectionType { + kTypeNotPresent = -1, + kRectangular = 0, + kEquirectangular = 1, + kCubeMap = 2, + kMesh = 3, + }; + static const float kValueNotPresent; + Projection() + : type(kTypeNotPresent), + private_data(NULL), + private_data_length(0), + pose_yaw(kValueNotPresent), + pose_pitch(kValueNotPresent), + pose_roll(kValueNotPresent) {} + ~Projection() { delete[] private_data; } + static bool Parse(IMkvReader* reader, long long element_start, + long long element_size, Projection** projection); + + ProjectionType type; + unsigned char* private_data; + size_t private_data_length; + float pose_yaw; + float pose_pitch; + float pose_roll; +}; + class VideoTrack : public Track { VideoTrack(const VideoTrack&); VideoTrack& operator=(const VideoTrack&); @@ -496,6 +525,8 @@ class VideoTrack : public Track { Colour* GetColour() const; + Projection* GetProjection() const; + private: long long m_width; long long m_height; @@ -507,6 +538,7 @@ class VideoTrack : public Track { double m_rate; Colour* m_colour; + Projection* m_projection; }; class AudioTrack : public Track { @@ -812,6 +844,8 @@ class SeekHead { long Parse(); struct Entry { + Entry(); + // the SeekHead entry payload long long id; long long pos; |