diff options
Diffstat (limited to 'core')
244 files changed, 7827 insertions, 3035 deletions
diff --git a/core/SCsub b/core/SCsub index c8e2e10b9f..c6d0b7e5b1 100644 --- a/core/SCsub +++ b/core/SCsub @@ -8,7 +8,6 @@ from platform_methods import run_in_subprocess env.core_sources = [] - # Generate global defaults gd_call = "" gd_inc = "" @@ -18,7 +17,7 @@ for x in env.global_defaults: gd_inc += '#include "platform/' + x + '/globals/global_defaults.h"\n' gd_call += "\tregister_" + x + "_global_defaults();\n" -gd_cpp = '#include "project_settings.h"\n' +gd_cpp = '#include "core/project_settings.h"\n' gd_cpp += gd_inc gd_cpp += "void ProjectSettings::register_global_defaults() {\n" + gd_call + "\n}\n" @@ -52,13 +51,17 @@ if ("SCRIPT_AES256_ENCRYPTION_KEY" in os.environ): # NOTE: It is safe to generate this file here, since this is still executed serially with open("script_encryption_key.gen.cpp", "w") as f: - f.write("#include \"project_settings.h\"\nuint8_t script_encryption_key[32]={" + txt + "};\n") + f.write("#include \"core/project_settings.h\"\nuint8_t script_encryption_key[32]={" + txt + "};\n") + +# Add required thirdparty code. +env_thirdparty = env.Clone() +env_thirdparty.disable_warnings() -# Add required thirdparty code. Header paths are hardcoded, we don't need to append +# Misc thirdparty code: header paths are hardcoded, we don't need to append # to the include path (saves a few chars on the compiler invocation for touchy MSVC...) -thirdparty_dir = "#thirdparty/misc/" -thirdparty_sources = [ +thirdparty_misc_dir = "#thirdparty/misc/" +thirdparty_misc_sources = [ # C sources "base64.c", "fastlz.c", @@ -72,10 +75,34 @@ thirdparty_sources = [ "pcg.cpp", "triangulator.cpp", ] -thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env.add_source_files(env.core_sources, thirdparty_sources) - -# Minizip library, can be unbundled in theory +thirdparty_misc_sources = [thirdparty_misc_dir + file for file in thirdparty_misc_sources] +env_thirdparty.add_source_files(env.core_sources, thirdparty_misc_sources) + +# Zlib library, can be unbundled +if env['builtin_zlib']: + thirdparty_zlib_dir = "#thirdparty/zlib/" + thirdparty_zlib_sources = [ + "adler32.c", + "compress.c", + "crc32.c", + "deflate.c", + "infback.c", + "inffast.c", + "inflate.c", + "inftrees.c", + "trees.c", + "uncompr.c", + "zutil.c", + ] + thirdparty_zlib_sources = [thirdparty_zlib_dir + file for file in thirdparty_zlib_sources] + + env_thirdparty.Append(CPPPATH=[thirdparty_zlib_dir]) + # Needs to be available in main env too + env.Append(CPPPATH=[thirdparty_zlib_dir]) + + env_thirdparty.add_source_files(env.core_sources, thirdparty_zlib_sources) + +# Minizip library, could be unbundled in theory # However, our version has some custom modifications, so it won't compile with the system one thirdparty_minizip_dir = "#thirdparty/minizip/" thirdparty_minizip_sources = [ @@ -84,15 +111,50 @@ thirdparty_minizip_sources = [ "zip.c", ] thirdparty_minizip_sources = [thirdparty_minizip_dir + file for file in thirdparty_minizip_sources] -env.add_source_files(env.core_sources, thirdparty_minizip_sources) - -if 'builtin_zstd' in env and env['builtin_zstd']: - SConscript("#thirdparty/zstd/SCsub") +env_thirdparty.add_source_files(env.core_sources, thirdparty_minizip_sources) + +# Zstd library, can be unbundled in theory +# though we currently use some private symbols +# https://github.com/godotengine/godot/issues/17374 +if env['builtin_zstd']: + thirdparty_zstd_dir = "#thirdparty/zstd/" + thirdparty_zstd_sources = [ + "common/entropy_common.c", + "common/error_private.c", + "common/fse_decompress.c", + "common/pool.c", + "common/threading.c", + "common/xxhash.c", + "common/zstd_common.c", + "compress/fse_compress.c", + "compress/huf_compress.c", + "compress/zstd_compress.c", + "compress/zstd_double_fast.c", + "compress/zstd_fast.c", + "compress/zstd_lazy.c", + "compress/zstd_ldm.c", + "compress/zstdmt_compress.c", + "compress/zstd_opt.c", + "decompress/huf_decompress.c", + "decompress/zstd_decompress.c", + ] + thirdparty_zstd_sources = [thirdparty_zstd_dir + file for file in thirdparty_zstd_sources] + + env_thirdparty.Append(CPPPATH=[thirdparty_zstd_dir, thirdparty_zstd_dir + "common"]) + env_thirdparty.Append(CCFLAGS="-DZSTD_STATIC_LINKING_ONLY") + env.Append(CPPPATH=thirdparty_zstd_dir) + # Also needed in main env includes will trigger warnings + env.Append(CCFLAGS="-DZSTD_STATIC_LINKING_ONLY") + + env_thirdparty.add_source_files(env.core_sources, thirdparty_zstd_sources) # Godot's own sources env.add_source_files(env.core_sources, "*.cpp") +# Certificates +env.Depends("#core/io/certs_compressed.gen.h", ["#thirdparty/certs/ca-certificates.crt", env.Value(env['builtin_certs']), env.Value(env['system_certs_path'])]) +env.CommandNoCache("#core/io/certs_compressed.gen.h", "#thirdparty/certs/ca-certificates.crt", run_in_subprocess(core_builders.make_certs_header)) # Make binders env.CommandNoCache(['method_bind.gen.inc', 'method_bind_ext.gen.inc'], 'make_binders.py', run_in_subprocess(make_binders.run)) @@ -114,10 +176,8 @@ SConscript('os/SCsub') SConscript('math/SCsub') SConscript('io/SCsub') SConscript('bind/SCsub') -SConscript('helper/SCsub') # Build it all as a library lib = env.add_library("core", env.core_sources) env.Prepend(LIBS=[lib]) -Export('env') diff --git a/core/allocators.h b/core/allocators.h index e17ab298d6..840b117958 100644 --- a/core/allocators.h +++ b/core/allocators.h @@ -31,7 +31,8 @@ #ifndef ALLOCATORS_H #define ALLOCATORS_H -#include "os/memory.h" +#include "core/os/memory.h" + template <int PREALLOC_COUNT = 64, int MAX_HANDS = 8> class BalloonAllocator { diff --git a/core/array.cpp b/core/array.cpp index 96e64294ed..9708452850 100644 --- a/core/array.cpp +++ b/core/array.cpp @@ -30,10 +30,10 @@ #include "array.h" -#include "hashfuncs.h" -#include "object.h" -#include "variant.h" -#include "vector.h" +#include "core/hashfuncs.h" +#include "core/object.h" +#include "core/variant.h" +#include "core/vector.h" class ArrayPrivate { public: @@ -259,7 +259,7 @@ Array &Array::sort_custom(Object *p_obj, const StringName &p_function) { ERR_FAIL_NULL_V(p_obj, *this); - SortArray<Variant, _ArrayVariantSortCustom> avs; + SortArray<Variant, _ArrayVariantSortCustom, true> avs; avs.compare.obj = p_obj; avs.compare.func = p_function; avs.sort(_p->array.ptrw(), _p->array.size()); @@ -355,11 +355,58 @@ Variant Array::pop_front() { return Variant(); } +Variant Array::min() const { + + Variant minval; + for (int i = 0; i < size(); i++) { + if (i == 0) { + minval = get(i); + } else { + bool valid; + Variant ret; + Variant test = get(i); + Variant::evaluate(Variant::OP_LESS, test, minval, ret, valid); + if (!valid) { + return Variant(); //not a valid comparison + } + if (bool(ret)) { + //is less + minval = test; + } + } + } + return minval; +} + +Variant Array::max() const { + + Variant maxval; + for (int i = 0; i < size(); i++) { + if (i == 0) { + maxval = get(i); + } else { + bool valid; + Variant ret; + Variant test = get(i); + Variant::evaluate(Variant::OP_GREATER, test, maxval, ret, valid); + if (!valid) { + return Variant(); //not a valid comparison + } + if (bool(ret)) { + //is less + maxval = test; + } + } + } + return maxval; +} + Array::Array(const Array &p_from) { _p = NULL; _ref(p_from); } + Array::Array() { _p = memnew(ArrayPrivate); diff --git a/core/array.h b/core/array.h index e549a886e6..fa5113a376 100644 --- a/core/array.h +++ b/core/array.h @@ -31,7 +31,8 @@ #ifndef ARRAY_H #define ARRAY_H -#include "typedefs.h" +#include "core/typedefs.h" + class Variant; class ArrayPrivate; class Object; @@ -90,6 +91,9 @@ public: Array duplicate(bool p_deep = false) const; + Variant min() const; + Variant max() const; + Array(const Array &p_from); Array(); ~Array(); diff --git a/core/bind/SCsub b/core/bind/SCsub index 4efc902717..1c5f954470 100644 --- a/core/bind/SCsub +++ b/core/bind/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.core_sources, "*.cpp") - -Export('env') diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index af1d49ae8c..0032c43179 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -30,14 +30,14 @@ #include "core_bind.h" +#include "core/io/file_access_compressed.h" +#include "core/io/file_access_encrypted.h" +#include "core/io/json.h" +#include "core/io/marshalls.h" +#include "core/math/geometry.h" +#include "core/os/keyboard.h" +#include "core/os/os.h" #include "core/project_settings.h" -#include "geometry.h" -#include "io/file_access_compressed.h" -#include "io/file_access_encrypted.h" -#include "io/json.h" -#include "io/marshalls.h" -#include "os/keyboard.h" -#include "os/os.h" #include "thirdparty/misc/base64.h" @@ -112,11 +112,22 @@ PoolStringArray _ResourceLoader::get_dependencies(const String &p_path) { return ret; }; +#ifndef DISABLE_DEPRECATED bool _ResourceLoader::has(const String &p_path) { + WARN_PRINTS("ResourceLoader.has() is deprecated, please replace it with the equivalent has_cached() or the new exists()."); + return has_cached(p_path); +} +#endif // DISABLE_DEPRECATED + +bool _ResourceLoader::has_cached(const String &p_path) { String local_path = ProjectSettings::get_singleton()->localize_path(p_path); return ResourceCache::has(local_path); -}; +} + +bool _ResourceLoader::exists(const String &p_path, const String &p_type_hint) { + return ResourceLoader::exists(p_path, p_type_hint); +} void _ResourceLoader::_bind_methods() { @@ -125,7 +136,11 @@ void _ResourceLoader::_bind_methods() { ClassDB::bind_method(D_METHOD("get_recognized_extensions_for_type", "type"), &_ResourceLoader::get_recognized_extensions_for_type); ClassDB::bind_method(D_METHOD("set_abort_on_missing_resources", "abort"), &_ResourceLoader::set_abort_on_missing_resources); ClassDB::bind_method(D_METHOD("get_dependencies", "path"), &_ResourceLoader::get_dependencies); + ClassDB::bind_method(D_METHOD("has_cached", "path"), &_ResourceLoader::has_cached); + ClassDB::bind_method(D_METHOD("exists", "path", "type_hint"), &_ResourceLoader::exists, DEFVAL("")); +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("has", "path"), &_ResourceLoader::has); +#endif // DISABLE_DEPRECATED } _ResourceLoader::_ResourceLoader() { @@ -165,6 +180,7 @@ void _ResourceSaver::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_OMIT_EDITOR_PROPERTIES); BIND_ENUM_CONSTANT(FLAG_SAVE_BIG_ENDIAN); BIND_ENUM_CONSTANT(FLAG_COMPRESS); + BIND_ENUM_CONSTANT(FLAG_REPLACE_SUBRESOURCE_PATHS); } _ResourceSaver::_ResourceSaver() { @@ -225,6 +241,14 @@ PoolStringArray _OS::get_connected_midi_inputs() { return OS::get_singleton()->get_connected_midi_inputs(); } +void _OS::open_midi_inputs() { + return OS::get_singleton()->open_midi_inputs(); +} + +void _OS::close_midi_inputs() { + return OS::get_singleton()->close_midi_inputs(); +} + void _OS::set_video_mode(const Size2 &p_size, bool p_fullscreen, bool p_resizeable, int p_screen) { OS::VideoMode vm; @@ -643,7 +667,7 @@ Dictionary _OS::get_time(bool utc) const { * * @return epoch calculated */ -uint64_t _OS::get_unix_time_from_datetime(Dictionary datetime) const { +int64_t _OS::get_unix_time_from_datetime(Dictionary datetime) const { // Bunch of conversion constants static const unsigned int SECONDS_PER_MINUTE = 60; @@ -688,13 +712,18 @@ uint64_t _OS::get_unix_time_from_datetime(Dictionary datetime) const { // Calculate all the seconds from months past in this year uint64_t SECONDS_FROM_MONTHS_PAST_THIS_YEAR = DAYS_PAST_THIS_YEAR_TABLE[LEAPYEAR(year)][month - 1] * SECONDS_PER_DAY; - uint64_t SECONDS_FROM_YEARS_PAST = 0; - for (unsigned int iyear = EPOCH_YR; iyear < year; iyear++) { - - SECONDS_FROM_YEARS_PAST += YEARSIZE(iyear) * SECONDS_PER_DAY; + int64_t SECONDS_FROM_YEARS_PAST = 0; + if (year >= EPOCH_YR) { + for (unsigned int iyear = EPOCH_YR; iyear < year; iyear++) { + SECONDS_FROM_YEARS_PAST += YEARSIZE(iyear) * SECONDS_PER_DAY; + } + } else { + for (unsigned int iyear = EPOCH_YR - 1; iyear >= year; iyear--) { + SECONDS_FROM_YEARS_PAST -= YEARSIZE(iyear) * SECONDS_PER_DAY; + } } - uint64_t epoch = + int64_t epoch = second + minute * SECONDS_PER_MINUTE + hour * SECONDS_PER_HOUR + @@ -717,34 +746,36 @@ uint64_t _OS::get_unix_time_from_datetime(Dictionary datetime) const { * * @return dictionary of date and time values */ -Dictionary _OS::get_datetime_from_unix_time(uint64_t unix_time_val) const { - - // Just fail if unix time is negative (when interpreted as an int). - // This means the user passed in a negative value by accident - ERR_EXPLAIN("unix_time_val was really huge!" + itos(unix_time_val) + " You probably passed in a negative value!"); - ERR_FAIL_COND_V((int64_t)unix_time_val < 0, Dictionary()); +Dictionary _OS::get_datetime_from_unix_time(int64_t unix_time_val) const { OS::Date date; OS::Time time; - unsigned long dayclock, dayno; + long dayclock, dayno; int year = EPOCH_YR; - dayclock = (unsigned long)unix_time_val % SECS_DAY; - dayno = (unsigned long)unix_time_val / SECS_DAY; + if (unix_time_val >= 0) { + dayno = unix_time_val / SECS_DAY; + dayclock = unix_time_val % SECS_DAY; + /* day 0 was a thursday */ + date.weekday = static_cast<OS::Weekday>((dayno + 4) % 7); + while (dayno >= YEARSIZE(year)) { + dayno -= YEARSIZE(year); + year++; + } + } else { + dayno = (unix_time_val - SECS_DAY + 1) / SECS_DAY; + dayclock = unix_time_val - dayno * SECS_DAY; + date.weekday = static_cast<OS::Weekday>((dayno - 3) % 7 + 7); + do { + year--; + dayno += YEARSIZE(year); + } while (dayno < 0); + } time.sec = dayclock % 60; time.min = (dayclock % 3600) / 60; time.hour = dayclock / 3600; - - /* day 0 was a thursday */ - date.weekday = static_cast<OS::Weekday>((dayno + 4) % 7); - - while (dayno >= YEARSIZE(year)) { - dayno -= YEARSIZE(year); - year++; - } - date.year = year; size_t imonth = 0; @@ -989,6 +1020,11 @@ void _OS::center_window() { OS::get_singleton()->center_window(); } +void _OS::move_window_to_foreground() { + + OS::get_singleton()->move_window_to_foreground(); +} + bool _OS::is_debug_build() const { #ifdef DEBUG_ENABLED @@ -1063,6 +1099,8 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("get_audio_driver_count"), &_OS::get_audio_driver_count); ClassDB::bind_method(D_METHOD("get_audio_driver_name", "driver"), &_OS::get_audio_driver_name); ClassDB::bind_method(D_METHOD("get_connected_midi_inputs"), &_OS::get_connected_midi_inputs); + ClassDB::bind_method(D_METHOD("open_midi_inputs"), &_OS::open_midi_inputs); + ClassDB::bind_method(D_METHOD("close_midi_inputs"), &_OS::close_midi_inputs); ClassDB::bind_method(D_METHOD("get_screen_count"), &_OS::get_screen_count); ClassDB::bind_method(D_METHOD("get_current_screen"), &_OS::get_current_screen); @@ -1088,6 +1126,7 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("request_attention"), &_OS::request_attention); ClassDB::bind_method(D_METHOD("get_real_window_size"), &_OS::get_real_window_size); ClassDB::bind_method(D_METHOD("center_window"), &_OS::center_window); + ClassDB::bind_method(D_METHOD("move_window_to_foreground"), &_OS::move_window_to_foreground); ClassDB::bind_method(D_METHOD("set_borderless_window", "borderless"), &_OS::set_borderless_window); ClassDB::bind_method(D_METHOD("get_borderless_window"), &_OS::get_borderless_window); @@ -1715,9 +1754,9 @@ String _File::get_line() const { return f->get_line(); } -Vector<String> _File::get_csv_line(String delim) const { +Vector<String> _File::get_csv_line(const String &p_delim) const { ERR_FAIL_COND_V(!f, Vector<String>()); - return f->get_csv_line(delim); + return f->get_csv_line(p_delim); } /**< use this for files WRITTEN in _big_ endian machines (ie, amiga/mac) @@ -1814,6 +1853,11 @@ void _File::store_line(const String &p_string) { f->store_line(p_string); } +void _File::store_csv_line(const Vector<String> &p_values, const String &p_delim) { + ERR_FAIL_COND(!f); + f->store_csv_line(p_values, p_delim); +} + void _File::store_buffer(const PoolVector<uint8_t> &p_buffer) { ERR_FAIL_COND(!f); @@ -1897,6 +1941,7 @@ void _File::_bind_methods() { ClassDB::bind_method(D_METHOD("get_real"), &_File::get_real); ClassDB::bind_method(D_METHOD("get_buffer", "len"), &_File::get_buffer); ClassDB::bind_method(D_METHOD("get_line"), &_File::get_line); + ClassDB::bind_method(D_METHOD("get_csv_line", "delim"), &_File::get_csv_line, DEFVAL(",")); ClassDB::bind_method(D_METHOD("get_as_text"), &_File::get_as_text); ClassDB::bind_method(D_METHOD("get_md5", "path"), &_File::get_md5); ClassDB::bind_method(D_METHOD("get_sha256", "path"), &_File::get_sha256); @@ -1904,7 +1949,6 @@ void _File::_bind_methods() { ClassDB::bind_method(D_METHOD("set_endian_swap", "enable"), &_File::set_endian_swap); ClassDB::bind_method(D_METHOD("get_error"), &_File::get_error); ClassDB::bind_method(D_METHOD("get_var"), &_File::get_var); - ClassDB::bind_method(D_METHOD("get_csv_line", "delim"), &_File::get_csv_line, DEFVAL(",")); ClassDB::bind_method(D_METHOD("store_8", "value"), &_File::store_8); ClassDB::bind_method(D_METHOD("store_16", "value"), &_File::store_16); @@ -1915,6 +1959,7 @@ void _File::_bind_methods() { ClassDB::bind_method(D_METHOD("store_real", "value"), &_File::store_real); ClassDB::bind_method(D_METHOD("store_buffer", "buffer"), &_File::store_buffer); ClassDB::bind_method(D_METHOD("store_line", "line"), &_File::store_line); + ClassDB::bind_method(D_METHOD("store_csv_line", "values", "delim"), &_File::store_csv_line, DEFVAL(",")); ClassDB::bind_method(D_METHOD("store_string", "string"), &_File::store_string); ClassDB::bind_method(D_METHOD("store_var", "value"), &_File::store_var); @@ -2455,7 +2500,7 @@ _Thread::~_Thread() { if (active) { ERR_EXPLAIN("Reference to a Thread object object was lost while the thread is still running..."); } - ERR_FAIL_COND(active == true); + ERR_FAIL_COND(active); } ///////////////////////////////////// @@ -2827,10 +2872,10 @@ void JSONParseResult::_bind_methods() { ClassDB::bind_method(D_METHOD("set_error_line", "error_line"), &JSONParseResult::set_error_line); ClassDB::bind_method(D_METHOD("set_result", "result"), &JSONParseResult::set_result); - ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "error", PROPERTY_HINT_NONE, "Error", PROPERTY_USAGE_CLASS_IS_ENUM), "set_error", "get_error"); - ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "error_string"), "set_error_string", "get_error_string"); - ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "error_line"), "set_error_line", "get_error_line"); - ADD_PROPERTYNZ(PropertyInfo(Variant::NIL, "result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), "set_result", "get_result"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "error", PROPERTY_HINT_NONE, "Error", PROPERTY_USAGE_CLASS_IS_ENUM), "set_error", "get_error"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "error_string"), "set_error_string", "get_error_string"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "error_line"), "set_error_line", "get_error_line"); + ADD_PROPERTY(PropertyInfo(Variant::NIL, "result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), "set_result", "get_result"); } void JSONParseResult::set_error(Error p_error) { diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 1729c23779..720b14bf56 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -31,15 +31,15 @@ #ifndef CORE_BIND_H #define CORE_BIND_H -#include "image.h" -#include "io/compression.h" -#include "io/resource_loader.h" -#include "io/resource_saver.h" -#include "os/dir_access.h" -#include "os/file_access.h" -#include "os/os.h" -#include "os/semaphore.h" -#include "os/thread.h" +#include "core/image.h" +#include "core/io/compression.h" +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "core/os/semaphore.h" +#include "core/os/thread.h" class _ResourceLoader : public Object { GDCLASS(_ResourceLoader, Object); @@ -55,7 +55,11 @@ public: PoolVector<String> get_recognized_extensions_for_type(const String &p_type); void set_abort_on_missing_resources(bool p_abort); PoolStringArray get_dependencies(const String &p_path); +#ifndef DISABLE_DEPRECATED bool has(const String &p_path); +#endif // DISABLE_DEPRECATED + bool has_cached(const String &p_path); + bool exists(const String &p_path, const String &p_type_hint = ""); _ResourceLoader(); }; @@ -76,6 +80,7 @@ public: FLAG_OMIT_EDITOR_PROPERTIES = 8, FLAG_SAVE_BIG_ENDIAN = 16, FLAG_COMPRESS = 32, + FLAG_REPLACE_SUBRESOURCE_PATHS = 64, }; static _ResourceSaver *get_singleton() { return singleton; } @@ -153,6 +158,8 @@ public: virtual String get_audio_driver_name(int p_driver) const; virtual PoolStringArray get_connected_midi_inputs(); + virtual void open_midi_inputs(); + virtual void close_midi_inputs(); virtual int get_screen_count() const; virtual int get_current_screen() const; @@ -178,6 +185,7 @@ public: virtual bool is_window_always_on_top() const; virtual void request_attention(); virtual void center_window(); + virtual void move_window_to_foreground(); virtual void set_borderless_window(bool p_borderless); virtual bool get_borderless_window() const; @@ -266,8 +274,8 @@ public: Dictionary get_date(bool utc) const; Dictionary get_time(bool utc) const; Dictionary get_datetime(bool utc) const; - Dictionary get_datetime_from_unix_time(uint64_t unix_time_val) const; - uint64_t get_unix_time_from_datetime(Dictionary datetime) const; + Dictionary get_datetime_from_unix_time(int64_t unix_time_val) const; + int64_t get_unix_time_from_datetime(Dictionary datetime) const; Dictionary get_time_zone_info() const; uint64_t get_unix_time() const; uint64_t get_system_time_secs() const; @@ -447,6 +455,7 @@ public: PoolVector<uint8_t> get_buffer(int p_length) const; ///< get an array of bytes String get_line() const; + Vector<String> get_csv_line(const String &p_delim = ",") const; String get_as_text() const; String get_md5(const String &p_path) const; String get_sha256(const String &p_path) const; @@ -472,12 +481,11 @@ public: void store_string(const String &p_string); void store_line(const String &p_string); + void store_csv_line(const Vector<String> &p_values, const String &p_delim = ","); virtual void store_pascal_string(const String &p_string); virtual String get_pascal_string(); - Vector<String> get_csv_line(String delim = ",") const; - void store_buffer(const PoolVector<uint8_t> &p_buffer); ///< store an array of bytes void store_var(const Variant &p_var); diff --git a/core/class_db.cpp b/core/class_db.cpp index 03b214aa41..052a4586fe 100644 --- a/core/class_db.cpp +++ b/core/class_db.cpp @@ -30,8 +30,9 @@ #include "class_db.h" -#include "os/mutex.h" -#include "version.h" +#include "core/engine.h" +#include "core/os/mutex.h" +#include "core/version.h" #define OBJTYPE_RLOCK RWLockRead _rw_lockr_(lock); #define OBJTYPE_WLOCK RWLockWrite _rw_lockw_(lock); @@ -512,7 +513,12 @@ Object *ClassDB::instance(const StringName &p_class) { ERR_FAIL_COND_V(ti->disabled, NULL); ERR_FAIL_COND_V(!ti->creation_func, NULL); } - +#ifdef TOOLS_ENABLED + if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) { + ERR_PRINTS("Class '" + String(p_class) + "' can only be instantiated by editor."); + return NULL; + } +#endif return ti->creation_func(); } bool ClassDB::can_instance(const StringName &p_class) { @@ -803,10 +809,10 @@ void ClassDB::add_signal(StringName p_class, const MethodInfo &p_signal) { ClassInfo *type = classes.getptr(p_class); ERR_FAIL_COND(!type); - ClassInfo *check = type; StringName sname = p_signal.name; -#ifdef DEBUG_METHODS_ENABLED +#ifdef DEBUG_METHODS_ENABLED + ClassInfo *check = type; while (check) { if (check->signal_map.has(sname)) { ERR_EXPLAIN("Type " + String(p_class) + " already has signal: " + String(sname)); @@ -930,9 +936,8 @@ void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, cons } #ifdef DEBUG_METHODS_ENABLED - if (type->property_setget.has(p_pinfo.name)) { - ERR_EXPLAIN("Object already has property: " + p_class); + ERR_EXPLAIN("Object " + p_class + " already has property: " + p_pinfo.name); ERR_FAIL(); } #endif @@ -1361,6 +1366,41 @@ void ClassDB::get_extensions_for_type(const StringName &p_class, List<String> *p } } +HashMap<StringName, HashMap<StringName, Variant> > ClassDB::default_values; + +Variant ClassDB::class_get_default_property_value(const StringName &p_class, const StringName &p_property) { + + if (!default_values.has(p_class)) { + + default_values[p_class] = HashMap<StringName, Variant>(); + + if (ClassDB::can_instance(p_class)) { + + Object *c = ClassDB::instance(p_class); + List<PropertyInfo> plist; + c->get_property_list(&plist); + for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { + if (E->get().usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR)) { + + Variant v = c->get(E->get().name); + default_values[p_class][E->get().name] = v; + } + } + memdelete(c); + } + } + + if (!default_values.has(p_class)) { + return Variant(); + } + + if (!default_values[p_class].has(p_property)) { + return Variant(); + } + + return default_values[p_class][p_property]; +} + RWLock *ClassDB::lock = NULL; void ClassDB::init() { @@ -1387,6 +1427,7 @@ void ClassDB::cleanup() { classes.clear(); resource_base_extensions.clear(); compat_classes.clear(); + default_values.clear(); memdelete(lock); } diff --git a/core/class_db.h b/core/class_db.h index f1d1879236..75f9e8d6a7 100644 --- a/core/class_db.h +++ b/core/class_db.h @@ -31,14 +31,18 @@ #ifndef CLASS_DB_H #define CLASS_DB_H -#include "method_bind.h" -#include "object.h" -#include "print_string.h" +#include "core/method_bind.h" +#include "core/object.h" +#include "core/print_string.h" /** @author Juan Linietsky <reduzio@gmail.com> */ +/** To bind more then 6 parameters include this: + * #include "core/method_bind_ext.gen.inc" + */ + #define DEFVAL(m_defval) (m_defval) //#define SIMPLE_METHODDEF @@ -157,6 +161,8 @@ public: static void _add_class2(const StringName &p_class, const StringName &p_inherits); + static HashMap<StringName, HashMap<StringName, Variant> > default_values; + public: // DO NOT USE THIS!!!!!! NEEDS TO BE PUBLIC BUT DO NOT USE NO MATTER WHAT!!! template <class T> @@ -348,6 +354,8 @@ public: static void get_enum_list(const StringName &p_class, List<StringName> *p_enums, bool p_no_inheritance = false); static void get_enum_constants(const StringName &p_class, const StringName &p_enum, List<StringName> *p_constants, bool p_no_inheritance = false); + static Variant class_get_default_property_value(const StringName &p_class, const StringName &p_property); + static StringName get_category(const StringName &p_node); static void set_class_enabled(StringName p_class, bool p_enable); diff --git a/core/color.cpp b/core/color.cpp index fcfcf20355..ac314417ec 100644 --- a/core/color.cpp +++ b/core/color.cpp @@ -30,10 +30,10 @@ #include "color.h" -#include "color_names.inc" -#include "map.h" -#include "math_funcs.h" -#include "print_string.h" +#include "core/color_names.inc" +#include "core/map.h" +#include "core/math/math_funcs.h" +#include "core/print_string.h" uint32_t Color::to_argb32() const { @@ -253,6 +253,21 @@ Color Color::hex64(uint64_t p_hex) { return Color(r, g, b, a); } +Color Color::from_rgbe9995(uint32_t p_rgbe) { + + float r = p_rgbe & 0x1ff; + float g = (p_rgbe >> 9) & 0x1ff; + float b = (p_rgbe >> 18) & 0x1ff; + float e = (p_rgbe >> 27); + float m = Math::pow(2, e - 15.0 - 9.0); + + float rd = r * m; + float gd = g * m; + float bd = b * m; + + return Color(rd, gd, bd, 1.0f); +} + static float _parse_col(const String &p_str, int p_ofs) { int ig = 0; @@ -453,7 +468,7 @@ String Color::to_html(bool p_alpha) const { return txt; } -Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) { +Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) const { p_h = Math::fmod(p_h * 360.0f, 360.0f); if (p_h < 0.0) @@ -506,8 +521,11 @@ Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) { return Color(m + r, m + g, m + b, p_a); } +// FIXME: Remove once Godot 3.1 has been released float Color::gray() const { + ERR_EXPLAIN("Color.gray() is deprecated and will be removed in a future version. Use Color.get_v() for a better grayscale approximation."); + WARN_DEPRECATED return (r + g + b) / 3.0; } diff --git a/core/color.h b/core/color.h index c0516e55fe..d6ad5f91c5 100644 --- a/core/color.h +++ b/core/color.h @@ -31,8 +31,8 @@ #ifndef COLOR_H #define COLOR_H -#include "math_funcs.h" -#include "ustring.h" +#include "core/math/math_funcs.h" +#include "core/ustring.h" /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -194,7 +194,8 @@ struct Color { static bool html_is_valid(const String &p_color); static Color named(const String &p_name); String to_html(bool p_alpha = true) const; - Color from_hsv(float p_h, float p_s, float p_v, float p_a); + Color from_hsv(float p_h, float p_s, float p_v, float p_a) const; + static Color from_rgbe9995(uint32_t p_color); _FORCE_INLINE_ bool operator<(const Color &p_color) const; //used in set keys operator String() const; diff --git a/core/color_names.inc b/core/color_names.inc index 3ae42648d0..e126bfe0f8 100644 --- a/core/color_names.inc +++ b/core/color_names.inc @@ -1,5 +1,5 @@ // Names from https://en.wikipedia.org/wiki/List_of_colors (through https://raw.githubusercontent.com/SuperUserNameMan/color_to_name/616a7cddafefda91478b7bc26167de97fb5badb1/godot_version.gd), slightly edited and normalized -#include "map.h" +#include "core/map.h" static Map<String, Color> _named_colors; static void _populate_named_colors() { diff --git a/core/command_queue_mt.cpp b/core/command_queue_mt.cpp index a39c920dfa..1b3a74bd0d 100644 --- a/core/command_queue_mt.cpp +++ b/core/command_queue_mt.cpp @@ -30,7 +30,7 @@ #include "command_queue_mt.h" -#include "os/os.h" +#include "core/os/os.h" void CommandQueueMT::lock() { diff --git a/core/command_queue_mt.h b/core/command_queue_mt.h index 7978eaa7bf..5911cf5006 100644 --- a/core/command_queue_mt.h +++ b/core/command_queue_mt.h @@ -31,11 +31,11 @@ #ifndef COMMAND_QUEUE_MT_H #define COMMAND_QUEUE_MT_H -#include "os/memory.h" -#include "os/mutex.h" -#include "os/semaphore.h" -#include "simple_type.h" -#include "typedefs.h" +#include "core/os/memory.h" +#include "core/os/mutex.h" +#include "core/os/semaphore.h" +#include "core/simple_type.h" +#include "core/typedefs.h" /** @author Juan Linietsky <reduzio@gmail.com> */ diff --git a/core/compressed_translation.cpp b/core/compressed_translation.cpp index 5c1fd26e2a..7dd5308fab 100644 --- a/core/compressed_translation.cpp +++ b/core/compressed_translation.cpp @@ -30,7 +30,7 @@ #include "compressed_translation.h" -#include "pair.h" +#include "core/pair.h" extern "C" { #include "thirdparty/misc/smaz.h" @@ -50,7 +50,6 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { int size = Math::larger_prime(keys.size()); - print_line("compressing keys: " + itos(keys.size())); Vector<Vector<Pair<int, CharString> > > buckets; Vector<Map<uint32_t, int> > table; Vector<uint32_t> hfunc_table; @@ -107,7 +106,6 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { } int bucket_table_size = 0; - print_line("total compressed string size: " + itos(total_compression_size) + " (" + itos(total_string_size) + " uncompressed)."); for (int i = 0; i < size; i++) { @@ -117,8 +115,6 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { if (b.size() == 0) continue; - //print_line("bucket: "+itos(i)+" - elements: "+itos(b.size())); - int d = 1; int item = 0; @@ -140,9 +136,6 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { bucket_table_size += 2 + b.size() * 4; } - print_line("bucket table size: " + itos(bucket_table_size * 4)); - print_line("hash table size: " + itos(size * 4)); - hash_table.resize(size); bucket_table.resize(bucket_table_size); @@ -178,8 +171,6 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { } } - print_line("total collisions: " + itos(collisions)); - strings.resize(total_compression_size); PoolVector<uint8_t>::Write cw = strings.write(); @@ -198,15 +189,11 @@ bool PHashTranslation::_set(const StringName &p_name, const Variant &p_value) { String name = p_name.operator String(); if (name == "hash_table") { hash_table = p_value; - //print_line("translation: loaded hash table of size: "+itos(hash_table.size())); } else if (name == "bucket_table") { bucket_table = p_value; - //print_line("translation: loaded bucket table of size: "+itos(bucket_table.size())); } else if (name == "strings") { strings = p_value; - //print_line("translation: loaded string table of size: "+itos(strings.size())); } else if (name == "load_from") { - //print_line("generating"); generate(p_value); } else return false; @@ -248,11 +235,7 @@ StringName PHashTranslation::get_message(const StringName &p_src_text) const { uint32_t p = htptr[h % htsize]; - //print_line("String: "+p_src_text.operator String()); - //print_line("Hash: "+itos(p)); - if (p == 0xFFFFFFFF) { - //print_line("GETMSG: Nothing!"); return StringName(); //nothing } @@ -271,9 +254,7 @@ StringName PHashTranslation::get_message(const StringName &p_src_text) const { } } - //print_line("bucket pos: "+itos(idx)); if (idx == -1) { - //print_line("GETMSG: Not in Bucket!"); return StringName(); } @@ -281,8 +262,6 @@ StringName PHashTranslation::get_message(const StringName &p_src_text) const { String rstr; rstr.parse_utf8(&sptr[bucket.elem[idx].str_offset], bucket.elem[idx].uncomp_size); - //print_line("Uncompressed, size: "+itos(bucket.elem[idx].comp_size)); - //print_line("Return: "+rstr); return rstr; } else { @@ -292,8 +271,6 @@ StringName PHashTranslation::get_message(const StringName &p_src_text) const { smaz_decompress(&sptr[bucket.elem[idx].str_offset], bucket.elem[idx].comp_size, uncomp.ptrw(), bucket.elem[idx].uncomp_size); String rstr; rstr.parse_utf8(uncomp.get_data()); - //print_line("Compressed, size: "+itos(bucket.elem[idx].comp_size)); - //print_line("Return: "+rstr); return rstr; } } diff --git a/core/compressed_translation.h b/core/compressed_translation.h index ccc47d0bf6..b3d193b478 100644 --- a/core/compressed_translation.h +++ b/core/compressed_translation.h @@ -31,7 +31,7 @@ #ifndef COMPRESSED_TRANSLATION_H #define COMPRESSED_TRANSLATION_H -#include "translation.h" +#include "core/translation.h" class PHashTranslation : public Translation { diff --git a/core/core_builders.py b/core/core_builders.py index 90e505aab9..f3a9e3b221 100644 --- a/core/core_builders.py +++ b/core/core_builders.py @@ -4,7 +4,40 @@ All such functions are invoked in a subprocess on Windows to prevent build flaki """ from platform_methods import subprocess_main -from compat import iteritems, itervalues, open_utf8, escape_string +from compat import iteritems, itervalues, open_utf8, escape_string, byte_to_str + + +def make_certs_header(target, source, env): + + src = source[0] + dst = target[0] + f = open(src, "rb") + g = open_utf8(dst, "w") + buf = f.read() + decomp_size = len(buf) + import zlib + buf = zlib.compress(buf) + + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef _CERTS_RAW_H\n") + g.write("#define _CERTS_RAW_H\n") + + # System certs path. Editor will use them if defined. (for package maintainers) + path = env['system_certs_path'] + g.write("#define _SYSTEM_CERTS_PATH \"%s\"\n" % str(path)) + if env['builtin_certs']: + # Defined here and not in env so changing it does not trigger a full rebuild. + g.write("#define BUILTIN_CERTS_ENABLED\n") + g.write("static const int _certs_compressed_size = " + str(len(buf)) + ";\n") + g.write("static const int _certs_uncompressed_size = " + str(decomp_size) + ";\n") + g.write("static const unsigned char _certs_compressed[] = {\n") + for i in range(len(buf)): + g.write("\t" + byte_to_str(buf[i]) + ",\n") + g.write("};\n") + g.write("#endif") + + g.close() + f.close() def make_authors_header(target, source, env): diff --git a/core/core_string_names.h b/core/core_string_names.h index dcbce14aac..3fdb240408 100644 --- a/core/core_string_names.h +++ b/core/core_string_names.h @@ -31,7 +31,7 @@ #ifndef CORE_STRING_NAMES_H #define CORE_STRING_NAMES_H -#include "string_db.h" +#include "core/string_db.h" class CoreStringNames { diff --git a/core/cowdata.h b/core/cowdata.h index 66e7d1c343..54ece4c856 100644 --- a/core/cowdata.h +++ b/core/cowdata.h @@ -31,8 +31,8 @@ #ifndef COWDATA_H_ #define COWDATA_H_ -#include "os/memory.h" -#include "safe_refcount.h" +#include "core/os/memory.h" +#include "core/safe_refcount.h" template <class T> class Vector; @@ -87,7 +87,10 @@ private: #if defined(_add_overflow) && defined(_mul_overflow) size_t o; size_t p; - if (_mul_overflow(p_elements, sizeof(T), &o)) return false; + if (_mul_overflow(p_elements, sizeof(T), &o)) { + *out = 0; + return false; + } *out = next_power_of_2(o); if (_add_overflow(o, static_cast<size_t>(32), &p)) return false; //no longer allocated here return true; @@ -100,6 +103,7 @@ private: } void _unref(void *p_data); + void _ref(const CowData *p_from); void _ref(const CowData &p_from); void _copy_on_write(); @@ -301,6 +305,11 @@ Error CowData<T>::resize(int p_size) { } template <class T> +void CowData<T>::_ref(const CowData *p_from) { + _ref(*p_from); +} + +template <class T> void CowData<T>::_ref(const CowData &p_from) { if (_ptr == p_from._ptr) diff --git a/core/dictionary.cpp b/core/dictionary.cpp index 42d9eab310..6a3ab82879 100644 --- a/core/dictionary.cpp +++ b/core/dictionary.cpp @@ -30,9 +30,9 @@ #include "dictionary.h" -#include "ordered_hash_map.h" -#include "safe_refcount.h" -#include "variant.h" +#include "core/ordered_hash_map.h" +#include "core/safe_refcount.h" +#include "core/variant.h" struct DictionaryPrivate { @@ -112,6 +112,15 @@ Variant Dictionary::get_valid(const Variant &p_key) const { return E.get(); } +Variant Dictionary::get(const Variant &p_key, const Variant &p_default) const { + const Variant *result = getptr(p_key); + if (!result) { + return p_default; + } + + return *result; +} + int Dictionary::size() const { return _p->variant_map.size(); @@ -135,12 +144,7 @@ bool Dictionary::has_all(const Array &p_keys) const { return true; } -void Dictionary::erase(const Variant &p_key) { - - _p->variant_map.erase(p_key); -} - -bool Dictionary::erase_checked(const Variant &p_key) { +bool Dictionary::erase(const Variant &p_key) { return _p->variant_map.erase(p_key); } @@ -150,6 +154,11 @@ bool Dictionary::operator==(const Dictionary &p_dictionary) const { return _p == p_dictionary._p; } +bool Dictionary::operator!=(const Dictionary &p_dictionary) const { + + return _p != p_dictionary._p; +} + void Dictionary::_ref(const Dictionary &p_from) const { //make a copy first (thread safe) diff --git a/core/dictionary.h b/core/dictionary.h index 00ec67fb99..b77cc55254 100644 --- a/core/dictionary.h +++ b/core/dictionary.h @@ -31,9 +31,10 @@ #ifndef DICTIONARY_H #define DICTIONARY_H -#include "array.h" -#include "list.h" -#include "ustring.h" +#include "core/array.h" +#include "core/list.h" +#include "core/ustring.h" + class Variant; struct DictionaryPrivate; @@ -57,6 +58,7 @@ public: Variant *getptr(const Variant &p_key); Variant get_valid(const Variant &p_key) const; + Variant get(const Variant &p_key, const Variant &p_default) const; int size() const; bool empty() const; @@ -65,10 +67,10 @@ public: bool has(const Variant &p_key) const; bool has_all(const Array &p_keys) const; - void erase(const Variant &p_key); - bool erase_checked(const Variant &p_key); + bool erase(const Variant &p_key); bool operator==(const Dictionary &p_dictionary) const; + bool operator!=(const Dictionary &p_dictionary) const; uint32_t hash() const; void operator=(const Dictionary &p_dictionary); diff --git a/core/dvector.h b/core/dvector.h index e03a755e6c..2830c57ec0 100644 --- a/core/dvector.h +++ b/core/dvector.h @@ -31,12 +31,12 @@ #ifndef DVECTOR_H #define DVECTOR_H -#include "os/copymem.h" -#include "os/memory.h" -#include "os/rw_lock.h" -#include "pool_allocator.h" -#include "safe_refcount.h" -#include "ustring.h" +#include "core/os/copymem.h" +#include "core/os/memory.h" +#include "core/os/rw_lock.h" +#include "core/pool_allocator.h" +#include "core/safe_refcount.h" +#include "core/ustring.h" struct MemoryPool { @@ -149,7 +149,7 @@ class PoolVector { } } - if (old_alloc->refcount.unref() == true) { + if (old_alloc->refcount.unref()) { //this should never happen but.. #ifdef DEBUG_ENABLED @@ -209,7 +209,7 @@ class PoolVector { if (!alloc) return; - if (alloc->refcount.unref() == false) { + if (!alloc->refcount.unref()) { alloc = NULL; return; } diff --git a/core/engine.cpp b/core/engine.cpp index 7c8024b946..fceb602dcf 100644 --- a/core/engine.cpp +++ b/core/engine.cpp @@ -30,11 +30,11 @@ #include "engine.h" -#include "authors.gen.h" -#include "donors.gen.h" -#include "license.gen.h" -#include "version.h" -#include "version_hash.gen.h" +#include "core/authors.gen.h" +#include "core/donors.gen.h" +#include "core/license.gen.h" +#include "core/version.h" +#include "core/version_hash.gen.h" void Engine::set_iterations_per_second(int p_ips) { diff --git a/core/engine.h b/core/engine.h index 031ba29cd6..82b1e5d681 100644 --- a/core/engine.h +++ b/core/engine.h @@ -31,10 +31,10 @@ #ifndef ENGINE_H #define ENGINE_H -#include "list.h" -#include "os/main_loop.h" -#include "ustring.h" -#include "vector.h" +#include "core/list.h" +#include "core/os/main_loop.h" +#include "core/ustring.h" +#include "core/vector.h" class Engine { @@ -125,6 +125,7 @@ public: String get_license_text() const; Engine(); + virtual ~Engine() {} }; #endif // ENGINE_H diff --git a/core/error_macros.cpp b/core/error_macros.cpp index 5786802930..c1a0dd0dfc 100644 --- a/core/error_macros.cpp +++ b/core/error_macros.cpp @@ -30,7 +30,7 @@ #include "error_macros.h" -#include "io/logger.h" +#include "core/io/logger.h" #include "os/os.h" bool _err_error_exists = false; diff --git a/core/error_macros.h b/core/error_macros.h index 3587e01d54..8cec18d24f 100644 --- a/core/error_macros.h +++ b/core/error_macros.h @@ -31,7 +31,7 @@ #ifndef ERROR_MACROS_H #define ERROR_MACROS_H -#include "typedefs.h" +#include "core/typedefs.h" /** * Error macros. Unlike exceptions and asserts, these macros try to maintain consistency and stability * inside the code. It is recommended to always return processable data, so in case of an error, the @@ -313,7 +313,7 @@ extern bool _err_error_exists; #define WARN_DEPRECATED \ { \ - static bool warning_shown = false; \ + static volatile bool warning_shown = false; \ if (!warning_shown) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future", ERR_HANDLER_WARNING); \ _err_error_exists = false; \ diff --git a/core/func_ref.h b/core/func_ref.h index 681fe747d6..930c376abf 100644 --- a/core/func_ref.h +++ b/core/func_ref.h @@ -31,7 +31,7 @@ #ifndef FUNC_REF_H #define FUNC_REF_H -#include "reference.h" +#include "core/reference.h" class FuncRef : public Reference { diff --git a/core/global_constants.cpp b/core/global_constants.cpp index 187813f9d0..7e9b8b393c 100644 --- a/core/global_constants.cpp +++ b/core/global_constants.cpp @@ -30,10 +30,10 @@ #include "global_constants.h" -#include "object.h" -#include "os/input_event.h" -#include "os/keyboard.h" -#include "variant.h" +#include "core/object.h" +#include "core/os/input_event.h" +#include "core/os/keyboard.h" +#include "core/variant.h" struct _GlobalConstant { @@ -532,6 +532,7 @@ void register_global_constants() { BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_GLOBAL_DIR); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_RESOURCE_TYPE); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_MULTILINE_TEXT); + BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_PLACEHOLDER_TEXT); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_COLOR_NO_ALPHA); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_IMAGE_COMPRESS_LOSSY); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS); @@ -546,8 +547,9 @@ void register_global_constants() { BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_INTERNATIONALIZED); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_GROUP); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_CATEGORY); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_STORE_IF_NONZERO); - BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_STORE_IF_NONONE); + //deprecated, replaced by ClassDB function to check default value + //BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_STORE_IF_NONZERO); + //BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_STORE_IF_NONONE); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_NO_INSTANCE_STATE); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_RESTART_IF_CHANGED); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_USAGE_SCRIPT_VARIABLE); diff --git a/core/global_constants.h b/core/global_constants.h index 76f618989c..48f16934a3 100644 --- a/core/global_constants.h +++ b/core/global_constants.h @@ -31,7 +31,7 @@ #ifndef GLOBAL_CONSTANTS_H #define GLOBAL_CONSTANTS_H -#include "string_db.h" +#include "core/string_db.h" class GlobalConstants { public: diff --git a/core/hash_map.h b/core/hash_map.h index 2df743ba7d..3869cd3c36 100644 --- a/core/hash_map.h +++ b/core/hash_map.h @@ -31,12 +31,12 @@ #ifndef HASH_MAP_H #define HASH_MAP_H -#include "error_macros.h" -#include "hashfuncs.h" -#include "list.h" -#include "math_funcs.h" -#include "os/memory.h" -#include "ustring.h" +#include "core/error_macros.h" +#include "core/hashfuncs.h" +#include "core/list.h" +#include "core/math/math_funcs.h" +#include "core/os/memory.h" +#include "core/ustring.h" /** * @class HashMap @@ -150,7 +150,7 @@ private: if (new_hash_table_power == -1) return; - Element **new_hash_table = memnew_arr(Element *, (1 << new_hash_table_power)); + Element **new_hash_table = memnew_arr(Element *, ((uint64_t)1 << new_hash_table_power)); if (!new_hash_table) { ERR_PRINT("Out of Memory"); @@ -230,7 +230,7 @@ private: if (!p_t.hash_table || p_t.hash_table_power == 0) return; /* not copying from empty table */ - hash_table = memnew_arr(Element *, 1 << p_t.hash_table_power); + hash_table = memnew_arr(Element *, (uint64_t)1 << p_t.hash_table_power); hash_table_power = p_t.hash_table_power; elements = p_t.elements; diff --git a/core/hashfuncs.h b/core/hashfuncs.h index 735e679d1e..457cfadc9c 100644 --- a/core/hashfuncs.h +++ b/core/hashfuncs.h @@ -31,12 +31,12 @@ #ifndef HASHFUNCS_H #define HASHFUNCS_H -#include "math_defs.h" -#include "math_funcs.h" -#include "node_path.h" -#include "string_db.h" -#include "typedefs.h" -#include "ustring.h" +#include "core/math/math_defs.h" +#include "core/math/math_funcs.h" +#include "core/node_path.h" +#include "core/string_db.h" +#include "core/typedefs.h" +#include "core/ustring.h" /** * Hashing functions diff --git a/core/helper/SCsub b/core/helper/SCsub deleted file mode 100644 index 4efc902717..0000000000 --- a/core/helper/SCsub +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env python - -Import('env') - -env.add_source_files(env.core_sources, "*.cpp") - -Export('env') diff --git a/core/image.cpp b/core/image.cpp index 19440d1718..dca8aedb8f 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -30,11 +30,12 @@ #include "image.h" +#include "core/hash_map.h" #include "core/io/image_loader.h" +#include "core/io/resource_loader.h" +#include "core/math/math_funcs.h" #include "core/os/copymem.h" -#include "hash_map.h" -#include "math_funcs.h" -#include "print_string.h" +#include "core/print_string.h" #include "thirdparty/misc/hq2x.h" @@ -306,6 +307,7 @@ void Image::_get_mipmap_offset_and_size(int p_mipmap, int &r_offset, int &r_widt r_width = w; r_height = h; } + int Image::get_mipmap_offset(int p_mipmap) const { ERR_FAIL_INDEX_V(p_mipmap, get_mipmap_count() + 1, -1); @@ -498,8 +500,6 @@ void Image::convert(Format p_new_format) { bool gen_mipmaps = mipmaps; - //mipmaps=false; - _copy_internals_from(new_img); if (gen_mipmaps) @@ -525,7 +525,7 @@ static double _bicubic_interp_kernel(double x) { return bc; } -template <int CC> +template <int CC, class T> static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { // get source image size @@ -556,7 +556,7 @@ static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_ // initial pixel value - uint8_t *__restrict dst = p_dst + (y * p_dst_width + x) * CC; + T *__restrict dst = ((T *)p_dst) + (y * p_dst_width + x) * CC; double color[CC]; for (int i = 0; i < CC; i++) { @@ -584,23 +584,32 @@ static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_ ox2 = xmax; // get pixel of original image - const uint8_t *__restrict p = p_src + (oy2 * p_src_width + ox2) * CC; + const T *__restrict p = ((T *)p_src) + (oy2 * p_src_width + ox2) * CC; for (int i = 0; i < CC; i++) { - - color[i] += p[i] * k2; + if (sizeof(T) == 2) { //half float + color[i] = Math::half_to_float(p[i]); + } else { + color[i] += p[i] * k2; + } } } } for (int i = 0; i < CC; i++) { - dst[i] = CLAMP(Math::fast_ftoi(color[i]), 0, 255); + if (sizeof(T) == 1) { //byte + dst[i] = CLAMP(Math::fast_ftoi(color[i]), 0, 255); + } else if (sizeof(T) == 2) { //half float + dst[i] = Math::make_half_float(color[i]); + } else { + dst[i] = color[i]; + } } } } } -template <int CC> +template <int CC, class T> static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { enum { @@ -640,22 +649,58 @@ static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict for (uint32_t l = 0; l < CC; l++) { - uint32_t p00 = p_src[y_ofs_up + src_xofs_left + l] << FRAC_BITS; - uint32_t p10 = p_src[y_ofs_up + src_xofs_right + l] << FRAC_BITS; - uint32_t p01 = p_src[y_ofs_down + src_xofs_left + l] << FRAC_BITS; - uint32_t p11 = p_src[y_ofs_down + src_xofs_right + l] << FRAC_BITS; - - uint32_t interp_up = p00 + (((p10 - p00) * src_xofs_frac) >> FRAC_BITS); - uint32_t interp_down = p01 + (((p11 - p01) * src_xofs_frac) >> FRAC_BITS); - uint32_t interp = interp_up + (((interp_down - interp_up) * src_yofs_frac) >> FRAC_BITS); - interp >>= FRAC_BITS; - p_dst[i * p_dst_width * CC + j * CC + l] = interp; + if (sizeof(T) == 1) { //uint8 + uint32_t p00 = p_src[y_ofs_up + src_xofs_left + l] << FRAC_BITS; + uint32_t p10 = p_src[y_ofs_up + src_xofs_right + l] << FRAC_BITS; + uint32_t p01 = p_src[y_ofs_down + src_xofs_left + l] << FRAC_BITS; + uint32_t p11 = p_src[y_ofs_down + src_xofs_right + l] << FRAC_BITS; + + uint32_t interp_up = p00 + (((p10 - p00) * src_xofs_frac) >> FRAC_BITS); + uint32_t interp_down = p01 + (((p11 - p01) * src_xofs_frac) >> FRAC_BITS); + uint32_t interp = interp_up + (((interp_down - interp_up) * src_yofs_frac) >> FRAC_BITS); + interp >>= FRAC_BITS; + p_dst[i * p_dst_width * CC + j * CC + l] = interp; + } else if (sizeof(T) == 2) { //half float + + float xofs_frac = float(src_xofs_frac) / (1 << FRAC_BITS); + float yofs_frac = float(src_yofs_frac) / (1 << FRAC_BITS); + const T *src = ((const T *)p_src); + T *dst = ((T *)p_dst); + + float p00 = Math::half_to_float(src[y_ofs_up + src_xofs_left + l]); + float p10 = Math::half_to_float(src[y_ofs_up + src_xofs_right + l]); + float p01 = Math::half_to_float(src[y_ofs_down + src_xofs_left + l]); + float p11 = Math::half_to_float(src[y_ofs_down + src_xofs_right + l]); + + float interp_up = p00 + (p10 - p00) * xofs_frac; + float interp_down = p01 + (p11 - p01) * xofs_frac; + float interp = interp_up + ((interp_down - interp_up) * yofs_frac); + + dst[i * p_dst_width * CC + j * CC + l] = Math::make_half_float(interp); + } else if (sizeof(T) == 4) { //float + + float xofs_frac = float(src_xofs_frac) / (1 << FRAC_BITS); + float yofs_frac = float(src_yofs_frac) / (1 << FRAC_BITS); + const T *src = ((const T *)p_src); + T *dst = ((T *)p_dst); + + float p00 = src[y_ofs_up + src_xofs_left + l]; + float p10 = src[y_ofs_up + src_xofs_right + l]; + float p01 = src[y_ofs_down + src_xofs_left + l]; + float p11 = src[y_ofs_down + src_xofs_right + l]; + + float interp_up = p00 + (p10 - p00) * xofs_frac; + float interp_down = p01 + (p11 - p01) * xofs_frac; + float interp = interp_up + ((interp_down - interp_up) * yofs_frac); + + dst[i * p_dst_width * CC + j * CC + l] = interp; + } } } } } -template <int CC> +template <int CC, class T> static void _scale_nearest(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { for (uint32_t i = 0; i < p_dst_height; i++) { @@ -670,8 +715,11 @@ static void _scale_nearest(const uint8_t *__restrict p_src, uint8_t *__restrict for (uint32_t l = 0; l < CC; l++) { - uint32_t p = p_src[y_ofs + src_xofs + l]; - p_dst[i * p_dst_width * CC + j * CC + l] = p; + const T *src = ((const T *)p_src); + T *dst = ((T *)p_dst); + + T p = src[y_ofs + src_xofs + l]; + dst[i * p_dst_width * CC + j * CC + l] = p; } } } @@ -732,9 +780,9 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { // Setup mipmap-aware scaling Image dst2; - int mip1; - int mip2; - float mip1_weight; + int mip1 = 0; + int mip2 = 0; + float mip1_weight = 0; if (mipmap_aware) { float avg_scale = ((float)p_width / width + (float)p_height / height) * 0.5f; if (avg_scale >= 1.0f) { @@ -750,6 +798,7 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { if (interpolate_mipmaps) { dst2.create(p_width, p_height, 0, format); } + bool had_mipmaps = mipmaps; if (interpolate_mipmaps && !had_mipmaps) { generate_mipmaps(); @@ -766,12 +815,30 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { case INTERPOLATE_NEAREST: { - switch (get_format_pixel_size(format)) { - case 1: _scale_nearest<1>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 2: _scale_nearest<2>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 3: _scale_nearest<3>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 4: _scale_nearest<4>(r_ptr, w_ptr, width, height, p_width, p_height); break; + if (format >= FORMAT_L8 && format <= FORMAT_RGBA8) { + switch (get_format_pixel_size(format)) { + case 1: _scale_nearest<1, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 2: _scale_nearest<2, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 3: _scale_nearest<3, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 4: _scale_nearest<4, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + } + } else if (format >= FORMAT_RF && format <= FORMAT_RGBAF) { + switch (get_format_pixel_size(format)) { + case 4: _scale_nearest<1, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 8: _scale_nearest<2, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 12: _scale_nearest<3, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 16: _scale_nearest<4, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + } + + } else if (format >= FORMAT_RH && format <= FORMAT_RGBAH) { + switch (get_format_pixel_size(format)) { + case 2: _scale_nearest<1, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 4: _scale_nearest<2, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 6: _scale_nearest<3, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 8: _scale_nearest<4, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + } } + } break; case INTERPOLATE_BILINEAR: case INTERPOLATE_TRILINEAR: { @@ -812,11 +879,27 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { } } - switch (get_format_pixel_size(format)) { - case 1: _scale_bilinear<1>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; - case 2: _scale_bilinear<2>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; - case 3: _scale_bilinear<3>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; - case 4: _scale_bilinear<4>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + if (format >= FORMAT_L8 && format <= FORMAT_RGBA8) { + switch (get_format_pixel_size(format)) { + case 1: _scale_bilinear<1, uint8_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 2: _scale_bilinear<2, uint8_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 3: _scale_bilinear<3, uint8_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 4: _scale_bilinear<4, uint8_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + } + } else if (format >= FORMAT_RF && format <= FORMAT_RGBAF) { + switch (get_format_pixel_size(format)) { + case 4: _scale_bilinear<1, float>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 8: _scale_bilinear<2, float>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 12: _scale_bilinear<3, float>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 16: _scale_bilinear<4, float>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + } + } else if (format >= FORMAT_RH && format <= FORMAT_RGBAH) { + switch (get_format_pixel_size(format)) { + case 2: _scale_bilinear<1, uint16_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 4: _scale_bilinear<2, uint16_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 6: _scale_bilinear<3, uint16_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 8: _scale_bilinear<4, uint16_t>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + } } } @@ -829,13 +912,28 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { } break; case INTERPOLATE_CUBIC: { - switch (get_format_pixel_size(format)) { - case 1: _scale_cubic<1>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 2: _scale_cubic<2>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 3: _scale_cubic<3>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 4: _scale_cubic<4>(r_ptr, w_ptr, width, height, p_width, p_height); break; + if (format >= FORMAT_L8 && format <= FORMAT_RGBA8) { + switch (get_format_pixel_size(format)) { + case 1: _scale_cubic<1, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 2: _scale_cubic<2, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 3: _scale_cubic<3, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 4: _scale_cubic<4, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + } + } else if (format >= FORMAT_RF && format <= FORMAT_RGBAF) { + switch (get_format_pixel_size(format)) { + case 4: _scale_cubic<1, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 8: _scale_cubic<2, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 12: _scale_cubic<3, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 16: _scale_cubic<4, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + } + } else if (format >= FORMAT_RH && format <= FORMAT_RGBAH) { + switch (get_format_pixel_size(format)) { + case 2: _scale_cubic<1, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 4: _scale_cubic<2, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 6: _scale_cubic<3, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 8: _scale_cubic<4, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + } } - } break; } @@ -853,6 +951,7 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { } void Image::crop_from_point(int p_x, int p_y, int p_width, int p_height) { + if (!_can_modify(format)) { ERR_EXPLAIN("Cannot crop in indexed, compressed or custom image formats."); ERR_FAIL(); @@ -898,7 +997,7 @@ void Image::crop_from_point(int p_x, int p_y, int p_width, int p_height) { } } - if (mipmaps > 0) + if (has_mipmaps()) dst.generate_mipmaps(); _copy_internals_from(dst); } @@ -915,10 +1014,10 @@ void Image::flip_y() { ERR_FAIL(); } - bool gm = mipmaps; - - if (gm) + bool used_mipmaps = has_mipmaps(); + if (used_mipmaps) { clear_mipmaps(); + } { PoolVector<uint8_t>::Write w = data.write(); @@ -939,8 +1038,9 @@ void Image::flip_y() { } } - if (gm) + if (used_mipmaps) { generate_mipmaps(); + } } void Image::flip_x() { @@ -950,9 +1050,10 @@ void Image::flip_x() { ERR_FAIL(); } - bool gm = mipmaps; - if (gm) + bool used_mipmaps = has_mipmaps(); + if (used_mipmaps) { clear_mipmaps(); + } { PoolVector<uint8_t>::Write w = data.write(); @@ -973,8 +1074,9 @@ void Image::flip_x() { } } - if (gm) + if (used_mipmaps) { generate_mipmaps(); + } } int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps) { @@ -987,8 +1089,10 @@ int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int & int pixsize = get_format_pixel_size(p_format); int pixshift = get_format_pixel_rshift(p_format); int block = get_format_block_size(p_format); - int minw, minh; - get_format_min_pixel_size(p_format, minw, minh); + //technically, you can still compress up to 1 px no matter the format, so commenting this + //int minw, minh; + //get_format_min_pixel_size(p_format, minw, minh); + int minw = 1, minh = 1; while (true) { @@ -1027,48 +1131,38 @@ bool Image::_can_modify(Format p_format) const { return p_format <= FORMAT_RGBE9995; } -template <int CC, bool renormalize> -static void _generate_po2_mipmap(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_width, uint32_t p_height) { +template <class Component, int CC, bool renormalize, + void (*average_func)(Component &, const Component &, const Component &, const Component &, const Component &), + void (*renormalize_func)(Component *)> +static void _generate_po2_mipmap(const Component *p_src, Component *p_dst, uint32_t p_width, uint32_t p_height) { //fast power of 2 mipmap generation - uint32_t dst_w = p_width >> 1; - uint32_t dst_h = p_height >> 1; + uint32_t dst_w = MAX(p_width >> 1, 1); + uint32_t dst_h = MAX(p_height >> 1, 1); + + int right_step = (p_width == 1) ? 0 : CC; + int down_step = (p_height == 1) ? 0 : (p_width * CC); for (uint32_t i = 0; i < dst_h; i++) { - const uint8_t *rup_ptr = &p_src[i * 2 * p_width * CC]; - const uint8_t *rdown_ptr = rup_ptr + p_width * CC; - uint8_t *dst_ptr = &p_dst[i * dst_w * CC]; + const Component *rup_ptr = &p_src[i * 2 * down_step]; + const Component *rdown_ptr = rup_ptr + down_step; + Component *dst_ptr = &p_dst[i * dst_w * CC]; uint32_t count = dst_w; while (count--) { for (int j = 0; j < CC; j++) { - - uint16_t val = 0; - val += rup_ptr[j]; - val += rup_ptr[j + CC]; - val += rdown_ptr[j]; - val += rdown_ptr[j + CC]; - dst_ptr[j] = val >> 2; + average_func(dst_ptr[j], rup_ptr[j], rup_ptr[j + right_step], rdown_ptr[j], rdown_ptr[j + right_step]); } if (renormalize) { - Vector3 n(dst_ptr[0] / 255.0, dst_ptr[1] / 255.0, dst_ptr[2] / 255.0); - n *= 2.0; - n -= Vector3(1, 1, 1); - n.normalize(); - n += Vector3(1, 1, 1); - n *= 0.5; - n *= 255; - dst_ptr[0] = CLAMP(int(n.x), 0, 255); - dst_ptr[1] = CLAMP(int(n.y), 0, 255); - dst_ptr[2] = CLAMP(int(n.z), 0, 255); + renormalize_func(dst_ptr); } dst_ptr += CC; - rup_ptr += CC * 2; - rdown_ptr += CC * 2; + rup_ptr += right_step * 2; + rdown_ptr += right_step * 2; } } } @@ -1077,12 +1171,13 @@ void Image::expand_x2_hq2x() { ERR_FAIL_COND(!_can_modify(format)); - Format current = format; - bool mm = has_mipmaps(); - if (mm) { + bool used_mipmaps = has_mipmaps(); + if (used_mipmaps) { clear_mipmaps(); } + Format current = format; + if (current != FORMAT_RGBA8) convert(FORMAT_RGBA8); @@ -1103,6 +1198,8 @@ void Image::expand_x2_hq2x() { if (current != FORMAT_RGBA8) convert(current); + // FIXME: This is likely meant to use "used_mipmaps" as defined above, but if we do, + // we end up with a regression: GH-22747 if (mipmaps) { generate_mipmaps(); } @@ -1148,11 +1245,23 @@ void Image::shrink_x2() { switch (format) { case FORMAT_L8: - case FORMAT_R8: _generate_po2_mipmap<1, false>(r.ptr(), w.ptr(), width, height); break; - case FORMAT_LA8: _generate_po2_mipmap<2, false>(r.ptr(), w.ptr(), width, height); break; - case FORMAT_RG8: _generate_po2_mipmap<2, false>(r.ptr(), w.ptr(), width, height); break; - case FORMAT_RGB8: _generate_po2_mipmap<3, false>(r.ptr(), w.ptr(), width, height); break; - case FORMAT_RGBA8: _generate_po2_mipmap<4, false>(r.ptr(), w.ptr(), width, height); break; + case FORMAT_R8: _generate_po2_mipmap<uint8_t, 1, false, Image::average_4_uint8, Image::renormalize_uint8>(r.ptr(), w.ptr(), width, height); break; + case FORMAT_LA8: _generate_po2_mipmap<uint8_t, 2, false, Image::average_4_uint8, Image::renormalize_uint8>(r.ptr(), w.ptr(), width, height); break; + case FORMAT_RG8: _generate_po2_mipmap<uint8_t, 2, false, Image::average_4_uint8, Image::renormalize_uint8>(r.ptr(), w.ptr(), width, height); break; + case FORMAT_RGB8: _generate_po2_mipmap<uint8_t, 3, false, Image::average_4_uint8, Image::renormalize_uint8>(r.ptr(), w.ptr(), width, height); break; + case FORMAT_RGBA8: _generate_po2_mipmap<uint8_t, 4, false, Image::average_4_uint8, Image::renormalize_uint8>(r.ptr(), w.ptr(), width, height); break; + + case FORMAT_RF: _generate_po2_mipmap<float, 1, false, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(r.ptr()), reinterpret_cast<float *>(w.ptr()), width, height); break; + case FORMAT_RGF: _generate_po2_mipmap<float, 2, false, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(r.ptr()), reinterpret_cast<float *>(w.ptr()), width, height); break; + case FORMAT_RGBF: _generate_po2_mipmap<float, 3, false, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(r.ptr()), reinterpret_cast<float *>(w.ptr()), width, height); break; + case FORMAT_RGBAF: _generate_po2_mipmap<float, 4, false, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(r.ptr()), reinterpret_cast<float *>(w.ptr()), width, height); break; + + case FORMAT_RH: _generate_po2_mipmap<uint16_t, 1, false, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(r.ptr()), reinterpret_cast<uint16_t *>(w.ptr()), width, height); break; + case FORMAT_RGH: _generate_po2_mipmap<uint16_t, 2, false, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(r.ptr()), reinterpret_cast<uint16_t *>(w.ptr()), width, height); break; + case FORMAT_RGBH: _generate_po2_mipmap<uint16_t, 3, false, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(r.ptr()), reinterpret_cast<uint16_t *>(w.ptr()), width, height); break; + case FORMAT_RGBAH: _generate_po2_mipmap<uint16_t, 4, false, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(r.ptr()), reinterpret_cast<uint16_t *>(w.ptr()), width, height); break; + + case FORMAT_RGBE9995: _generate_po2_mipmap<uint32_t, 1, false, Image::average_4_rgbe9995, Image::renormalize_rgbe9995>(reinterpret_cast<const uint32_t *>(r.ptr()), reinterpret_cast<uint32_t *>(w.ptr()), width, height); break; default: {} } } @@ -1214,7 +1323,7 @@ Error Image::generate_mipmaps(bool p_renormalize) { int prev_h = height; int prev_w = width; - for (int i = 1; i < mmcount; i++) { + for (int i = 1; i <= mmcount; i++) { int ofs, w, h; _get_mipmap_offset_and_size(i, ofs, w, h); @@ -1222,21 +1331,68 @@ Error Image::generate_mipmaps(bool p_renormalize) { switch (format) { case FORMAT_L8: - case FORMAT_R8: _generate_po2_mipmap<1, false>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; + case FORMAT_R8: _generate_po2_mipmap<uint8_t, 1, false, Image::average_4_uint8, Image::renormalize_uint8>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; case FORMAT_LA8: - case FORMAT_RG8: _generate_po2_mipmap<2, false>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; + case FORMAT_RG8: _generate_po2_mipmap<uint8_t, 2, false, Image::average_4_uint8, Image::renormalize_uint8>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; case FORMAT_RGB8: if (p_renormalize) - _generate_po2_mipmap<3, true>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); + _generate_po2_mipmap<uint8_t, 3, true, Image::average_4_uint8, Image::renormalize_uint8>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); else - _generate_po2_mipmap<3, false>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); + _generate_po2_mipmap<uint8_t, 3, false, Image::average_4_uint8, Image::renormalize_uint8>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; case FORMAT_RGBA8: if (p_renormalize) - _generate_po2_mipmap<4, true>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); + _generate_po2_mipmap<uint8_t, 4, true, Image::average_4_uint8, Image::renormalize_uint8>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); + else + _generate_po2_mipmap<uint8_t, 4, false, Image::average_4_uint8, Image::renormalize_uint8>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); + break; + case FORMAT_RF: + _generate_po2_mipmap<float, 1, false, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(&wp[prev_ofs]), reinterpret_cast<float *>(&wp[ofs]), prev_w, prev_h); + break; + case FORMAT_RGF: + _generate_po2_mipmap<float, 2, false, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(&wp[prev_ofs]), reinterpret_cast<float *>(&wp[ofs]), prev_w, prev_h); + break; + case FORMAT_RGBF: + if (p_renormalize) + _generate_po2_mipmap<float, 3, true, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(&wp[prev_ofs]), reinterpret_cast<float *>(&wp[ofs]), prev_w, prev_h); + else + _generate_po2_mipmap<float, 3, false, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(&wp[prev_ofs]), reinterpret_cast<float *>(&wp[ofs]), prev_w, prev_h); + + break; + case FORMAT_RGBAF: + if (p_renormalize) + _generate_po2_mipmap<float, 4, true, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(&wp[prev_ofs]), reinterpret_cast<float *>(&wp[ofs]), prev_w, prev_h); + else + _generate_po2_mipmap<float, 4, false, Image::average_4_float, Image::renormalize_float>(reinterpret_cast<const float *>(&wp[prev_ofs]), reinterpret_cast<float *>(&wp[ofs]), prev_w, prev_h); + + break; + case FORMAT_RH: + _generate_po2_mipmap<uint16_t, 1, false, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(&wp[prev_ofs]), reinterpret_cast<uint16_t *>(&wp[ofs]), prev_w, prev_h); + break; + case FORMAT_RGH: + _generate_po2_mipmap<uint16_t, 2, false, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(&wp[prev_ofs]), reinterpret_cast<uint16_t *>(&wp[ofs]), prev_w, prev_h); + break; + case FORMAT_RGBH: + if (p_renormalize) + _generate_po2_mipmap<uint16_t, 3, true, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(&wp[prev_ofs]), reinterpret_cast<uint16_t *>(&wp[ofs]), prev_w, prev_h); else - _generate_po2_mipmap<4, false>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); + _generate_po2_mipmap<uint16_t, 3, false, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(&wp[prev_ofs]), reinterpret_cast<uint16_t *>(&wp[ofs]), prev_w, prev_h); + + break; + case FORMAT_RGBAH: + if (p_renormalize) + _generate_po2_mipmap<uint16_t, 4, true, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(&wp[prev_ofs]), reinterpret_cast<uint16_t *>(&wp[ofs]), prev_w, prev_h); + else + _generate_po2_mipmap<uint16_t, 4, false, Image::average_4_half, Image::renormalize_half>(reinterpret_cast<const uint16_t *>(&wp[prev_ofs]), reinterpret_cast<uint16_t *>(&wp[ofs]), prev_w, prev_h); + + break; + case FORMAT_RGBE9995: + if (p_renormalize) + _generate_po2_mipmap<uint32_t, 1, true, Image::average_4_rgbe9995, Image::renormalize_rgbe9995>(reinterpret_cast<const uint32_t *>(&wp[prev_ofs]), reinterpret_cast<uint32_t *>(&wp[ofs]), prev_w, prev_h); + else + _generate_po2_mipmap<uint32_t, 1, false, Image::average_4_rgbe9995, Image::renormalize_rgbe9995>(reinterpret_cast<const uint32_t *>(&wp[prev_ofs]), reinterpret_cast<uint32_t *>(&wp[ofs]), prev_w, prev_h); + break; default: {} } @@ -1304,7 +1460,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0); if (size != p_data.size()) { - ERR_EXPLAIN("Expected data size of " + itos(size) + " in Image::create()"); + ERR_EXPLAIN("Expected data size of " + itos(size) + " bytes in Image::create(), got instead " + itos(p_data.size()) + " bytes."); ERR_FAIL_COND(p_data.size() != size); } @@ -1317,7 +1473,8 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma void Image::create(const char **p_xpm) { - int size_width, size_height; + int size_width = 0; + int size_height = 0; int pixelchars = 0; mipmaps = false; bool has_alpha = false; @@ -1333,8 +1490,8 @@ void Image::create(const char **p_xpm) { int line = 0; HashMap<String, Color> colormap; - int colormap_size; - uint32_t pixel_size; + int colormap_size = 0; + uint32_t pixel_size = 0; PoolVector<uint8_t>::Write w; while (status != DONE) { @@ -1580,7 +1737,11 @@ Image::AlphaMode Image::detect_alpha() const { } Error Image::load(const String &p_path) { - +#ifdef DEBUG_ENABLED + if (p_path.begins_with("res://") && ResourceLoader::exists(p_path)) { + WARN_PRINTS("Loaded resource as image file, this will not work on export: '" + p_path + "'. Instead, import the image file as an Image resource and load it normally as a resource."); + } +#endif return ImageLoader::load_image(p_path, this); } @@ -1592,10 +1753,10 @@ Error Image::save_png(const String &p_path) const { return save_png_func(p_path, Ref<Image>((Image *)this)); } -int Image::get_image_data_size(int p_width, int p_height, Format p_format, int p_mipmaps) { +int Image::get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps) { int mm; - return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmaps); + return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmaps ? -1 : 0); } int Image::get_image_required_mipmaps(int p_width, int p_height, Format p_format) { @@ -1605,14 +1766,25 @@ int Image::get_image_required_mipmaps(int p_width, int p_height, Format p_format return mm; } +int Image::get_image_mipmap_offset(int p_width, int p_height, Format p_format, int p_mipmap) { + + if (p_mipmap <= 0) { + return 0; + } + int mm; + return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmap - 1); +} + bool Image::is_compressed() const { return format > FORMAT_RGBE9995; } Error Image::decompress() { - if (format >= FORMAT_DXT1 && format <= FORMAT_BPTC_RGBFU && _image_decompress_bc) + if (format >= FORMAT_DXT1 && format <= FORMAT_RGTC_RG && _image_decompress_bc) _image_decompress_bc(this); + else if (format >= FORMAT_BPTC_RGBA && format <= FORMAT_BPTC_RGBFU && _image_decompress_bptc) + _image_decompress_bptc(this); else if (format >= FORMAT_PVRTC2 && format <= FORMAT_PVRTC4A && _image_decompress_pvrtc) _image_decompress_pvrtc(this); else if (format == FORMAT_ETC && _image_decompress_etc1) @@ -1631,7 +1803,7 @@ Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_loss case COMPRESS_S3TC: { ERR_FAIL_COND_V(!_image_compress_bc_func, ERR_UNAVAILABLE); - _image_compress_bc_func(this, p_source); + _image_compress_bc_func(this, p_lossy_quality, p_source); } break; case COMPRESS_PVRTC2: { @@ -1653,6 +1825,11 @@ Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_loss ERR_FAIL_COND_V(!_image_compress_etc2_func, ERR_UNAVAILABLE); _image_compress_etc2_func(this, p_lossy_quality, p_source); } break; + case COMPRESS_BPTC: { + + ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE); + _image_compress_bptc_func(this, p_lossy_quality, p_source); + } break; } return OK; @@ -1754,7 +1931,8 @@ void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Po if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) return; - Rect2i dest_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest, clipped_src_rect.size)); + Point2 src_underscan = Point2(MIN(0, p_src_rect.position.x), MIN(0, p_src_rect.position.y)); + Rect2i dest_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest - src_underscan, clipped_src_rect.size)); PoolVector<uint8_t>::Write wp = data.write(); uint8_t *dst_data_ptr = wp.ptr(); @@ -1808,7 +1986,8 @@ void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, co if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) return; - Rect2i dest_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest, clipped_src_rect.size)); + Point2 src_underscan = Point2(MIN(0, p_src_rect.position.x), MIN(0, p_src_rect.position.y)); + Rect2i dest_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest - src_underscan, clipped_src_rect.size)); PoolVector<uint8_t>::Write wp = data.write(); uint8_t *dst_data_ptr = wp.ptr(); @@ -1865,7 +2044,8 @@ void Image::blend_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const P if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) return; - Rect2i dest_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest, clipped_src_rect.size)); + Point2 src_underscan = Point2(MIN(0, p_src_rect.position.x), MIN(0, p_src_rect.position.y)); + Rect2i dest_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest - src_underscan, clipped_src_rect.size)); lock(); Ref<Image> img = p_src; @@ -1919,7 +2099,8 @@ void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, c if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) return; - Rect2i dest_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest, clipped_src_rect.size)); + Point2 src_underscan = Point2(MIN(0, p_src_rect.position.x), MIN(0, p_src_rect.position.y)); + Rect2i dest_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest - src_underscan, clipped_src_rect.size)); lock(); Ref<Image> img = p_src; @@ -1989,13 +2170,15 @@ ImageMemLoadFunc Image::_png_mem_loader_func = NULL; ImageMemLoadFunc Image::_jpg_mem_loader_func = NULL; ImageMemLoadFunc Image::_webp_mem_loader_func = NULL; -void (*Image::_image_compress_bc_func)(Image *, Image::CompressSource) = NULL; +void (*Image::_image_compress_bc_func)(Image *, float, Image::CompressSource) = NULL; +void (*Image::_image_compress_bptc_func)(Image *, float, Image::CompressSource) = NULL; void (*Image::_image_compress_pvrtc2_func)(Image *) = NULL; void (*Image::_image_compress_pvrtc4_func)(Image *) = NULL; void (*Image::_image_compress_etc1_func)(Image *, float) = NULL; void (*Image::_image_compress_etc2_func)(Image *, float, Image::CompressSource) = NULL; void (*Image::_image_decompress_pvrtc)(Image *) = NULL; void (*Image::_image_decompress_bc)(Image *) = NULL; +void (*Image::_image_decompress_bptc)(Image *) = NULL; void (*Image::_image_decompress_etc1)(Image *) = NULL; void (*Image::_image_decompress_etc2)(Image *) = NULL; @@ -2179,18 +2362,7 @@ Color Image::get_pixel(int p_x, int p_y) const { return Color(Math::half_to_float(r), Math::half_to_float(g), Math::half_to_float(b), Math::half_to_float(a)); } break; case FORMAT_RGBE9995: { - uint32_t rgbe = ((uint32_t *)ptr)[ofs]; - float r = rgbe & 0x1ff; - float g = (rgbe >> 9) & 0x1ff; - float b = (rgbe >> 18) & 0x1ff; - float e = (rgbe >> 27); - float m = Math::pow(2, e - 15.0 - 9.0); - ; - float rd = r * m; - float gd = g * m; - float bd = b * m; - - return Color(rd, gd, bd, 1.0); + return Color::from_rgbe9995(((uint32_t *)ptr)[ofs]); } break; default: { @@ -2224,10 +2396,10 @@ void Image::set_pixel(int p_x, int p_y, const Color &p_color) { switch (format) { case FORMAT_L8: { - ptr[ofs] = uint8_t(CLAMP(p_color.gray() * 255.0, 0, 255)); + ptr[ofs] = uint8_t(CLAMP(p_color.get_v() * 255.0, 0, 255)); } break; case FORMAT_LA8: { - ptr[ofs * 2 + 0] = uint8_t(CLAMP(p_color.gray() * 255.0, 0, 255)); + ptr[ofs * 2 + 0] = uint8_t(CLAMP(p_color.get_v() * 255.0, 0, 255)); ptr[ofs * 2 + 1] = uint8_t(CLAMP(p_color.a * 255.0, 0, 255)); } break; case FORMAT_R8: { @@ -2376,6 +2548,17 @@ Image::DetectChannels Image::get_detected_channels() { return DETECTED_RGBA; } +void Image::optimize_channels() { + switch (get_detected_channels()) { + case DETECTED_L: convert(FORMAT_L8); break; + case DETECTED_LA: convert(FORMAT_LA8); break; + case DETECTED_R: convert(FORMAT_R8); break; + case DETECTED_RG: convert(FORMAT_RG8); break; + case DETECTED_RGB: convert(FORMAT_RGB8); break; + case DETECTED_RGBA: convert(FORMAT_RGBA8); break; + } +} + void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("get_width"), &Image::get_width); @@ -2508,11 +2691,16 @@ void Image::_bind_methods() { BIND_ENUM_CONSTANT(COMPRESS_SOURCE_NORMAL); } -void Image::set_compress_bc_func(void (*p_compress_func)(Image *, CompressSource)) { +void Image::set_compress_bc_func(void (*p_compress_func)(Image *, float, CompressSource)) { _image_compress_bc_func = p_compress_func; } +void Image::set_compress_bptc_func(void (*p_compress_func)(Image *, float, CompressSource)) { + + _image_compress_bptc_func = p_compress_func; +} + void Image::normalmap_to_xy() { convert(Image::FORMAT_RGBA8); @@ -2766,6 +2954,55 @@ Error Image::_load_from_buffer(const PoolVector<uint8_t> &p_array, ImageMemLoadF return OK; } +void Image::average_4_uint8(uint8_t &p_out, const uint8_t &p_a, const uint8_t &p_b, const uint8_t &p_c, const uint8_t &p_d) { + p_out = static_cast<uint8_t>((p_a + p_b + p_c + p_d + 2) >> 2); +} + +void Image::average_4_float(float &p_out, const float &p_a, const float &p_b, const float &p_c, const float &p_d) { + p_out = (p_a + p_b + p_c + p_d) * 0.25f; +} + +void Image::average_4_half(uint16_t &p_out, const uint16_t &p_a, const uint16_t &p_b, const uint16_t &p_c, const uint16_t &p_d) { + p_out = Math::make_half_float((Math::half_to_float(p_a) + Math::half_to_float(p_b) + Math::half_to_float(p_c) + Math::half_to_float(p_d)) * 0.25f); +} + +void Image::average_4_rgbe9995(uint32_t &p_out, const uint32_t &p_a, const uint32_t &p_b, const uint32_t &p_c, const uint32_t &p_d) { + p_out = ((Color::from_rgbe9995(p_a) + Color::from_rgbe9995(p_b) + Color::from_rgbe9995(p_c) + Color::from_rgbe9995(p_d)) * 0.25f).to_rgbe9995(); +} + +void Image::renormalize_uint8(uint8_t *p_rgb) { + Vector3 n(p_rgb[0] / 255.0, p_rgb[1] / 255.0, p_rgb[2] / 255.0); + n *= 2.0; + n -= Vector3(1, 1, 1); + n.normalize(); + n += Vector3(1, 1, 1); + n *= 0.5; + n *= 255; + p_rgb[0] = CLAMP(int(n.x), 0, 255); + p_rgb[1] = CLAMP(int(n.y), 0, 255); + p_rgb[2] = CLAMP(int(n.z), 0, 255); +} + +void Image::renormalize_float(float *p_rgb) { + Vector3 n(p_rgb[0], p_rgb[1], p_rgb[2]); + n.normalize(); + p_rgb[0] = n.x; + p_rgb[1] = n.y; + p_rgb[2] = n.z; +} + +void Image::renormalize_half(uint16_t *p_rgb) { + Vector3 n(Math::half_to_float(p_rgb[0]), Math::half_to_float(p_rgb[1]), Math::half_to_float(p_rgb[2])); + n.normalize(); + p_rgb[0] = Math::make_half_float(n.x); + p_rgb[1] = Math::make_half_float(n.y); + p_rgb[2] = Math::make_half_float(n.z); +} + +void Image::renormalize_rgbe9995(uint32_t *p_rgb) { + // Never used +} + Image::Image(const uint8_t *p_mem_png_jpg, int p_len) { width = 0; diff --git a/core/image.h b/core/image.h index 8c4854e053..0770eb953e 100644 --- a/core/image.h +++ b/core/image.h @@ -31,10 +31,10 @@ #ifndef IMAGE_H #define IMAGE_H -#include "color.h" -#include "dvector.h" -#include "math_2d.h" -#include "resource.h" +#include "core/color.h" +#include "core/dvector.h" +#include "core/math/rect2.h" +#include "core/resource.h" /** * @author Juan Linietsky <reduzio@gmail.com> @@ -116,7 +116,8 @@ public: enum CompressSource { COMPRESS_SOURCE_GENERIC, COMPRESS_SOURCE_SRGB, - COMPRESS_SOURCE_NORMAL + COMPRESS_SOURCE_NORMAL, + COMPRESS_SOURCE_LAYERED, }; //some functions provided by something else @@ -125,7 +126,8 @@ public: static ImageMemLoadFunc _jpg_mem_loader_func; static ImageMemLoadFunc _webp_mem_loader_func; - static void (*_image_compress_bc_func)(Image *, CompressSource p_source); + static void (*_image_compress_bc_func)(Image *, float, CompressSource p_source); + static void (*_image_compress_bptc_func)(Image *, float p_lossy_quality, CompressSource p_source); static void (*_image_compress_pvrtc2_func)(Image *); static void (*_image_compress_pvrtc4_func)(Image *); static void (*_image_compress_etc1_func)(Image *, float); @@ -133,6 +135,7 @@ public: static void (*_image_decompress_pvrtc)(Image *); static void (*_image_decompress_bc)(Image *); + static void (*_image_decompress_bptc)(Image *); static void (*_image_decompress_etc1)(Image *); static void (*_image_decompress_etc2)(Image *); @@ -181,6 +184,15 @@ private: Error _load_from_buffer(const PoolVector<uint8_t> &p_array, ImageMemLoadFunc p_loader); + static void average_4_uint8(uint8_t &p_out, const uint8_t &p_a, const uint8_t &p_b, const uint8_t &p_c, const uint8_t &p_d); + static void average_4_float(float &p_out, const float &p_a, const float &p_b, const float &p_c, const float &p_d); + static void average_4_half(uint16_t &p_out, const uint16_t &p_a, const uint16_t &p_b, const uint16_t &p_c, const uint16_t &p_d); + static void average_4_rgbe9995(uint32_t &p_out, const uint32_t &p_a, const uint32_t &p_b, const uint32_t &p_c, const uint32_t &p_d); + static void renormalize_uint8(uint8_t *p_rgb); + static void renormalize_float(float *p_rgb); + static void renormalize_half(uint16_t *p_rgb); + static void renormalize_rgbe9995(uint32_t *p_rgb); + public: int get_width() const; ///< Get image width int get_height() const; ///< Get image height @@ -272,8 +284,9 @@ public: static int get_format_block_size(Format p_format); static void get_format_min_pixel_size(Format p_format, int &r_w, int &r_h); - static int get_image_data_size(int p_width, int p_height, Format p_format, int p_mipmaps = 0); + static int get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps = false); static int get_image_required_mipmaps(int p_width, int p_height, Format p_format); + static int get_image_mipmap_offset(int p_width, int p_height, Format p_format, int p_mipmap); enum CompressMode { COMPRESS_S3TC, @@ -281,6 +294,7 @@ public: COMPRESS_PVRTC4, COMPRESS_ETC, COMPRESS_ETC2, + COMPRESS_BPTC }; Error compress(CompressMode p_mode = COMPRESS_S3TC, CompressSource p_source = COMPRESS_SOURCE_GENERIC, float p_lossy_quality = 0.7); @@ -303,7 +317,8 @@ public: Rect2 get_used_rect() const; Ref<Image> get_rect(const Rect2 &p_area) const; - static void set_compress_bc_func(void (*p_compress_func)(Image *, CompressSource)); + static void set_compress_bc_func(void (*p_compress_func)(Image *, float, CompressSource)); + static void set_compress_bptc_func(void (*p_compress_func)(Image *, float, CompressSource)); static String get_format_name(Format p_format); Error load_png_from_buffer(const PoolVector<uint8_t> &p_array); @@ -329,6 +344,7 @@ public: }; DetectChannels get_detected_channels(); + void optimize_channels(); Color get_pixelv(const Point2 &p_src) const; Color get_pixel(int p_x, int p_y) const; diff --git a/core/input_map.cpp b/core/input_map.cpp index d33f40cbcf..b88d99470a 100644 --- a/core/input_map.cpp +++ b/core/input_map.cpp @@ -30,8 +30,8 @@ #include "input_map.h" -#include "os/keyboard.h" -#include "project_settings.h" +#include "core/os/keyboard.h" +#include "core/project_settings.h" InputMap *InputMap::singleton = NULL; @@ -44,7 +44,7 @@ void InputMap::_bind_methods() { ClassDB::bind_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::add_action, DEFVAL(0.5f)); ClassDB::bind_method(D_METHOD("erase_action", "action"), &InputMap::erase_action); - ClassDB::bind_method(D_METHOD("action_set_deadzone", "deadzone"), &InputMap::action_set_deadzone); + ClassDB::bind_method(D_METHOD("action_set_deadzone", "action", "deadzone"), &InputMap::action_set_deadzone); ClassDB::bind_method(D_METHOD("action_add_event", "action", "event"), &InputMap::action_add_event); ClassDB::bind_method(D_METHOD("action_has_event", "action", "event"), &InputMap::action_has_event); ClassDB::bind_method(D_METHOD("action_erase_event", "action", "event"), &InputMap::action_erase_event); @@ -199,6 +199,10 @@ bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const Str Ref<InputEventAction> input_event_action = p_event; if (input_event_action.is_valid()) { + if (p_pressed != NULL) + *p_pressed = input_event_action->is_pressed(); + if (p_strength != NULL) + *p_strength = (*p_pressed) ? 1.0f : 0.0f; return input_event_action->get_action() == p_action; } diff --git a/core/input_map.h b/core/input_map.h index bdec75c65b..8f286277d1 100644 --- a/core/input_map.h +++ b/core/input_map.h @@ -31,8 +31,8 @@ #ifndef INPUT_MAP_H #define INPUT_MAP_H -#include "object.h" -#include "os/input_event.h" +#include "core/object.h" +#include "core/os/input_event.h" class InputMap : public Object { diff --git a/core/io/SCsub b/core/io/SCsub index 79b56cb716..1c5f954470 100644 --- a/core/io/SCsub +++ b/core/io/SCsub @@ -3,6 +3,3 @@ Import('env') env.add_source_files(env.core_sources, "*.cpp") - -Export('env') - diff --git a/core/io/compression.cpp b/core/io/compression.cpp index bc3bfcf356..3c0b6541bd 100644 --- a/core/io/compression.cpp +++ b/core/io/compression.cpp @@ -29,9 +29,10 @@ /*************************************************************************/ #include "compression.h" -#include "os/copymem.h" -#include "project_settings.h" -#include "zip_io.h" + +#include "core/io/zip_io.h" +#include "core/os/copymem.h" +#include "core/project_settings.h" #include "thirdparty/misc/fastlz.h" @@ -174,7 +175,7 @@ int Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p } break; case MODE_ZSTD: { ZSTD_DCtx *dctx = ZSTD_createDCtx(); - if (zstd_long_distance_matching) ZSTD_DCtx_setMaxWindowSize(dctx, 1 << zstd_window_log_size); + if (zstd_long_distance_matching) ZSTD_DCtx_setMaxWindowSize(dctx, (size_t)1 << zstd_window_log_size); int ret = ZSTD_decompressDCtx(dctx, p_dst, p_dst_max_size, p_src, p_src_size); ZSTD_freeDCtx(dctx); return ret; diff --git a/core/io/compression.h b/core/io/compression.h index a0ccd539cb..2f770e6aee 100644 --- a/core/io/compression.h +++ b/core/io/compression.h @@ -31,7 +31,7 @@ #ifndef COMPRESSION_H #define COMPRESSION_H -#include "typedefs.h" +#include "core/typedefs.h" class Compression { diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp index aa06ae5cc0..e2bc0f5d8f 100644 --- a/core/io/config_file.cpp +++ b/core/io/config_file.cpp @@ -29,9 +29,10 @@ /*************************************************************************/ #include "config_file.h" -#include "os/file_access.h" -#include "os/keyboard.h" -#include "variant_parser.h" + +#include "core/os/file_access.h" +#include "core/os/keyboard.h" +#include "core/variant_parser.h" PoolStringArray ConfigFile::_get_sections() const { diff --git a/core/io/config_file.h b/core/io/config_file.h index ac749bed76..2bc439d413 100644 --- a/core/io/config_file.h +++ b/core/io/config_file.h @@ -32,7 +32,7 @@ #define CONFIG_FILE_H #include "core/ordered_hash_map.h" -#include "reference.h" +#include "core/reference.h" class ConfigFile : public Reference { diff --git a/core/io/file_access_buffered.cpp b/core/io/file_access_buffered.cpp index dcaf99e24a..d44c8a9585 100644 --- a/core/io/file_access_buffered.cpp +++ b/core/io/file_access_buffered.cpp @@ -30,9 +30,7 @@ #include "file_access_buffered.h" -#include <string.h> - -#include "error_macros.h" +#include "core/error_macros.h" Error FileAccessBuffered::set_error(Error p_error) const { diff --git a/core/io/file_access_buffered.h b/core/io/file_access_buffered.h index f4ed47d6bc..a36a9a4603 100644 --- a/core/io/file_access_buffered.h +++ b/core/io/file_access_buffered.h @@ -31,10 +31,9 @@ #ifndef FILE_ACCESS_BUFFERED_H #define FILE_ACCESS_BUFFERED_H -#include "os/file_access.h" - -#include "dvector.h" -#include "ustring.h" +#include "core/dvector.h" +#include "core/os/file_access.h" +#include "core/ustring.h" class FileAccessBuffered : public FileAccess { diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index d6547ba19f..645d97ae7e 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -29,7 +29,9 @@ /*************************************************************************/ #include "file_access_compressed.h" -#include "print_string.h" + +#include "core/print_string.h" + void FileAccessCompressed::configure(const String &p_magic, Compression::Mode p_mode, int p_block_size) { magic = p_magic.ascii().get_data(); @@ -291,7 +293,6 @@ uint8_t FileAccessCompressed::get_8() const { } else { read_block--; at_end = true; - ret = 0; } } diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h index 587f58a7c6..5be42a6d32 100644 --- a/core/io/file_access_compressed.h +++ b/core/io/file_access_compressed.h @@ -31,8 +31,8 @@ #ifndef FILE_ACCESS_COMPRESSED_H #define FILE_ACCESS_COMPRESSED_H -#include "io/compression.h" -#include "os/file_access.h" +#include "core/io/compression.h" +#include "core/os/file_access.h" class FileAccessCompressed : public FileAccess { diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index bb7a444ccc..28bf55b962 100644 --- a/core/io/file_access_encrypted.cpp +++ b/core/io/file_access_encrypted.cpp @@ -30,9 +30,9 @@ #include "file_access_encrypted.h" +#include "core/os/copymem.h" +#include "core/print_string.h" #include "core/variant.h" -#include "os/copymem.h" -#include "print_string.h" #include "thirdparty/misc/aes256.h" #include "thirdparty/misc/md5.h" @@ -43,7 +43,6 @@ Error FileAccessEncrypted::open_and_parse(FileAccess *p_base, const Vector<uint8_t> &p_key, Mode p_mode) { - //print_line("open and parse!"); ERR_FAIL_COND_V(file != NULL, ERR_ALREADY_IN_USE); ERR_FAIL_COND_V(p_key.size() != 32, ERR_INVALID_PARAMETER); diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h index b9365a9fd0..873dbfa7e8 100644 --- a/core/io/file_access_encrypted.h +++ b/core/io/file_access_encrypted.h @@ -31,7 +31,7 @@ #ifndef FILE_ACCESS_ENCRYPTED_H #define FILE_ACCESS_ENCRYPTED_H -#include "os/file_access.h" +#include "core/os/file_access.h" class FileAccessEncrypted : public FileAccess { public: diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp index c4eb2848b1..4c2aa4294d 100644 --- a/core/io/file_access_memory.cpp +++ b/core/io/file_access_memory.cpp @@ -30,10 +30,10 @@ #include "file_access_memory.h" -#include "map.h" -#include "os/copymem.h" -#include "os/dir_access.h" -#include "project_settings.h" +#include "core/map.h" +#include "core/os/copymem.h" +#include "core/os/dir_access.h" +#include "core/project_settings.h" static Map<String, Vector<uint8_t> > *files = NULL; diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h index 2136f4cc0c..e474fb0d05 100644 --- a/core/io/file_access_memory.h +++ b/core/io/file_access_memory.h @@ -31,7 +31,7 @@ #ifndef FILE_ACCESS_MEMORY_H #define FILE_ACCESS_MEMORY_H -#include "os/file_access.h" +#include "core/os/file_access.h" class FileAccessMemory : public FileAccess { diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp index e0a2dbf507..b9544ac166 100644 --- a/core/io/file_access_network.cpp +++ b/core/io/file_access_network.cpp @@ -29,10 +29,11 @@ /*************************************************************************/ #include "file_access_network.h" -#include "io/ip.h" -#include "marshalls.h" -#include "os/os.h" -#include "project_settings.h" + +#include "core/io/ip.h" +#include "core/io/marshalls.h" +#include "core/os/os.h" +#include "core/project_settings.h" //#define DEBUG_PRINT(m_p) print_line(m_p) //#define DEBUG_TIME(m_what) printf("MS: %s - %lli\n",m_what,OS::get_singleton()->get_ticks_usec()); @@ -93,8 +94,6 @@ void FileAccessNetworkClient::_thread_func() { DEBUG_TIME("sem_unlock"); //DEBUG_PRINT("semwait returned "+itos(werr)); DEBUG_PRINT("MUTEX LOCK " + itos(lockcount)); - DEBUG_PRINT("POPO"); - DEBUG_PRINT("PEPE"); lock_mutex(); DEBUG_PRINT("MUTEX PASS"); @@ -228,7 +227,7 @@ FileAccessNetworkClient::FileAccessNetworkClient() { quit = false; singleton = this; last_id = 0; - client = Ref<StreamPeerTCP>(StreamPeerTCP::create_ref()); + client.instance(); sem = Semaphore::create(); lockcount = 0; } @@ -501,8 +500,9 @@ uint64_t FileAccessNetwork::_get_modified_time(const String &p_file) { void FileAccessNetwork::configure() { GLOBAL_DEF("network/remote_fs/page_size", 65536); + ProjectSettings::get_singleton()->set_custom_property_info("network/remote_fs/page_size", PropertyInfo(Variant::INT, "network/remote_fs/page_size", PROPERTY_HINT_RANGE, "1,65536,1,or_greater")); //is used as denominator and can't be zero GLOBAL_DEF("network/remote_fs/page_read_ahead", 4); - GLOBAL_DEF("network/remote_fs/max_pages", 20); + ProjectSettings::get_singleton()->set_custom_property_info("network/remote_fs/page_read_ahead", PropertyInfo(Variant::INT, "network/remote_fs/page_read_ahead", PROPERTY_HINT_RANGE, "0,8,1,or_greater")); } FileAccessNetwork::FileAccessNetwork() { @@ -520,7 +520,6 @@ FileAccessNetwork::FileAccessNetwork() { nc->unlock_mutex(); page_size = GLOBAL_GET("network/remote_fs/page_size"); read_ahead = GLOBAL_GET("network/remote_fs/page_read_ahead"); - max_pages = GLOBAL_GET("network/remote_fs/max_pages"); last_activity_val = 0; waiting_on_page = -1; last_page = -1; diff --git a/core/io/file_access_network.h b/core/io/file_access_network.h index be9bdb1af6..c929e8446d 100644 --- a/core/io/file_access_network.h +++ b/core/io/file_access_network.h @@ -31,10 +31,10 @@ #ifndef FILE_ACCESS_NETWORK_H #define FILE_ACCESS_NETWORK_H -#include "io/stream_peer_tcp.h" -#include "os/file_access.h" -#include "os/semaphore.h" -#include "os/thread.h" +#include "core/io/stream_peer_tcp.h" +#include "core/os/file_access.h" +#include "core/os/semaphore.h" +#include "core/os/thread.h" class FileAccessNetwork; @@ -47,8 +47,6 @@ class FileAccessNetworkClient { int size; }; - int ml; - List<BlockRequest> block_requests; Semaphore *sem; @@ -100,7 +98,6 @@ class FileAccessNetwork : public FileAccess { int page_size; int read_ahead; - int max_pages; mutable int waiting_on_page; mutable int last_activity_val; diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index efb4c7a073..3823285792 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "file_access_pack.h" -#include "version.h" + +#include "core/version.h" #include <stdio.h> @@ -168,11 +169,11 @@ bool PackedSourcePCK::try_open_pack(const String &p_path) { uint32_t version = f->get_32(); uint32_t ver_major = f->get_32(); uint32_t ver_minor = f->get_32(); - uint32_t ver_rev = f->get_32(); + f->get_32(); // ver_rev ERR_EXPLAIN("Pack version unsupported: " + itos(version)); ERR_FAIL_COND_V(version != PACK_VERSION, false); - ERR_EXPLAIN("Pack created with a newer version of the engine: " + itos(ver_major) + "." + itos(ver_minor) + "." + itos(ver_rev)); + ERR_EXPLAIN("Pack created with a newer version of the engine: " + itos(ver_major) + "." + itos(ver_minor)); ERR_FAIL_COND_V(ver_major > VERSION_MAJOR || (ver_major == VERSION_MAJOR && ver_minor > VERSION_MINOR), false); for (int i = 0; i < 16; i++) { @@ -454,7 +455,7 @@ String DirAccessPack::get_current_dir() { while (pd->parent) { pd = pd->parent; - p = pd->name + "/" + p; + p = pd->name.plus_file(p); } return "res://" + p; diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index 8a40e6d78c..9e31bcf88a 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -31,11 +31,11 @@ #ifndef FILE_ACCESS_PACK_H #define FILE_ACCESS_PACK_H -#include "list.h" -#include "map.h" -#include "os/dir_access.h" -#include "os/file_access.h" -#include "print_string.h" +#include "core/list.h" +#include "core/map.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" +#include "core/print_string.h" class PackSource; @@ -175,7 +175,6 @@ public: FileAccess *PackedData::try_open_path(const String &p_path) { - //print_line("try open path " + p_path); PathMD5 pmd5(p_path.md5_buffer()); Map<PathMD5, PackedFile>::Element *E = files.find(pmd5); if (!E) diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h index df83575f6a..9bb1ad221a 100644 --- a/core/io/file_access_zip.h +++ b/core/io/file_access_zip.h @@ -34,7 +34,7 @@ #define FILE_ACCESS_ZIP_H #include "core/io/file_access_pack.h" -#include "map.h" +#include "core/map.h" #include "thirdparty/minizip/unzip.h" @@ -90,8 +90,6 @@ class FileAccessZip : public FileAccess { mutable bool at_eof; - ZipArchive *archive; - public: virtual Error _open(const String &p_path, int p_mode_flags); ///< open a file virtual void close(); ///< close a file diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index 2425bb6d69..36dd688e77 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -29,8 +29,9 @@ /*************************************************************************/ #include "http_client.h" -#include "io/stream_peer_ssl.h" -#include "version.h" + +#include "core/io/stream_peer_ssl.h" +#include "core/version.h" const char *HTTPClient::_methods[METHOD_MAX] = { "GET", @@ -274,7 +275,7 @@ void HTTPClient::close() { response_headers.clear(); response_str.clear(); - body_size = 0; + body_size = -1; body_left = 0; chunk_left = 0; read_until_eof = false; @@ -348,7 +349,7 @@ Error HTTPClient::poll() { } if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) { - // Handshake has been successfull + // Handshake has been successful handshaking = false; status = STATUS_CONNECTED; return OK; @@ -373,7 +374,20 @@ Error HTTPClient::poll() { } break; } } break; + case STATUS_BODY: case STATUS_CONNECTED: { + // Check if we are still connected + if (ssl) { + Ref<StreamPeerSSL> tmp = connection; + tmp->poll(); + if (tmp->get_status() != StreamPeerSSL::STATUS_CONNECTED) { + status = STATUS_CONNECTION_ERROR; + return ERR_CONNECTION_ERROR; + } + } else if (tcp_connection->get_status() != StreamPeerTCP::STATUS_CONNECTED) { + status = STATUS_CONNECTION_ERROR; + return ERR_CONNECTION_ERROR; + } // Connection established, requests can now be made return OK; } break; @@ -403,7 +417,7 @@ Error HTTPClient::poll() { String response; response.parse_utf8((const char *)response_str.ptr()); Vector<String> responses = response.split("\n"); - body_size = 0; + body_size = -1; chunked = false; body_left = 0; chunk_left = 0; @@ -447,7 +461,7 @@ Error HTTPClient::poll() { } } - if (body_size || chunked) { + if (body_size != -1 || chunked) { status = STATUS_BODY; } else if (!keep_alive) { @@ -467,7 +481,8 @@ Error HTTPClient::poll() { case STATUS_DISCONNECTED: { return ERR_UNCONFIGURED; } break; - case STATUS_CONNECTION_ERROR: { + case STATUS_CONNECTION_ERROR: + case STATUS_SSL_HANDSHAKE_ERROR: { return ERR_CONNECTION_ERROR; } break; case STATUS_CANT_CONNECT: { @@ -664,11 +679,24 @@ Error HTTPClient::_get_http_data(uint8_t *p_buffer, int p_bytes, int &r_received if (blocking) { - Error err = connection->get_data(p_buffer, p_bytes); - if (err == OK) - r_received = p_bytes; - else - r_received = 0; + // We can't use StreamPeer.get_data, since when reaching EOF we will get an + // error without knowing how many bytes we received. + Error err = ERR_FILE_EOF; + int read = 0; + int left = p_bytes; + r_received = 0; + while (left > 0) { + err = connection->get_partial_data(p_buffer + r_received, left, read); + if (err == OK) { + r_received += read; + } else if (err == ERR_FILE_EOF) { + r_received += read; + return err; + } else { + return err; + } + left -= read; + } return err; } else { return connection->get_partial_data(p_buffer, p_bytes, r_received); @@ -682,11 +710,11 @@ void HTTPClient::set_read_chunk_size(int p_size) { HTTPClient::HTTPClient() { - tcp_connection = StreamPeerTCP::create_ref(); + tcp_connection.instance(); resolving = IP::RESOLVER_INVALID_ID; status = STATUS_DISCONNECTED; conn_port = -1; - body_size = 0; + body_size = -1; chunked = false; body_left = 0; read_until_eof = false; diff --git a/core/io/http_client.h b/core/io/http_client.h index 82b56b01db..9e674e4c1d 100644 --- a/core/io/http_client.h +++ b/core/io/http_client.h @@ -31,10 +31,10 @@ #ifndef HTTP_CLIENT_H #define HTTP_CLIENT_H -#include "io/ip.h" -#include "io/stream_peer.h" -#include "io/stream_peer_tcp.h" -#include "reference.h" +#include "core/io/ip.h" +#include "core/io/stream_peer.h" +#include "core/io/stream_peer_tcp.h" +#include "core/reference.h" class HTTPClient : public Reference { diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp index 614fbb771f..e4fbb0247d 100644 --- a/core/io/image_loader.cpp +++ b/core/io/image_loader.cpp @@ -30,7 +30,8 @@ #include "image_loader.h" -#include "print_string.h" +#include "core/print_string.h" + bool ImageFormatLoader::recognize(const String &p_extension) const { List<String> extensions; @@ -59,7 +60,7 @@ Error ImageLoader::load_image(String p_file, Ref<Image> p_image, FileAccess *p_c String extension = p_file.get_extension(); - for (int i = 0; i < loader_count; i++) { + for (int i = 0; i < loader.size(); i++) { if (!loader[i]->recognize(extension)) continue; @@ -82,30 +83,45 @@ Error ImageLoader::load_image(String p_file, Ref<Image> p_image, FileAccess *p_c void ImageLoader::get_recognized_extensions(List<String> *p_extensions) { - for (int i = 0; i < loader_count; i++) { + for (int i = 0; i < loader.size(); i++) { loader[i]->get_recognized_extensions(p_extensions); } } -bool ImageLoader::recognize(const String &p_extension) { +ImageFormatLoader *ImageLoader::recognize(const String &p_extension) { - for (int i = 0; i < loader_count; i++) { + for (int i = 0; i < loader.size(); i++) { if (loader[i]->recognize(p_extension)) - return true; + return loader[i]; } - return false; + return NULL; } -ImageFormatLoader *ImageLoader::loader[MAX_LOADERS]; -int ImageLoader::loader_count = 0; +Vector<ImageFormatLoader *> ImageLoader::loader; void ImageLoader::add_image_format_loader(ImageFormatLoader *p_loader) { - ERR_FAIL_COND(loader_count >= MAX_LOADERS); - loader[loader_count++] = p_loader; + loader.push_back(p_loader); +} + +void ImageLoader::remove_image_format_loader(ImageFormatLoader *p_loader) { + + loader.erase(p_loader); +} + +const Vector<ImageFormatLoader *> &ImageLoader::get_image_format_loaders() { + + return loader; +} + +void ImageLoader::cleanup() { + + while (loader.size()) { + remove_image_format_loader(loader[0]); + } } ///////////////// @@ -117,7 +133,6 @@ RES ResourceFormatLoaderImage::load(const String &p_path, const String &p_origin if (r_error) { *r_error = ERR_CANT_OPEN; } - memdelete(f); return RES(); } @@ -137,7 +152,7 @@ RES ResourceFormatLoaderImage::load(const String &p_path, const String &p_origin int idx = -1; - for (int i = 0; i < ImageLoader::loader_count; i++) { + for (int i = 0; i < ImageLoader::loader.size(); i++) { if (ImageLoader::loader[i]->recognize(extension)) { idx = i; break; @@ -185,5 +200,5 @@ bool ResourceFormatLoaderImage::handles_type(const String &p_type) const { String ResourceFormatLoaderImage::get_resource_type(const String &p_path) const { - return "Image"; + return p_path.get_extension().to_lower() == "image" ? "Image" : String(); } diff --git a/core/io/image_loader.h b/core/io/image_loader.h index fbb654c326..7a58d46f93 100644 --- a/core/io/image_loader.h +++ b/core/io/image_loader.h @@ -31,11 +31,11 @@ #ifndef IMAGE_LOADER_H #define IMAGE_LOADER_H -#include "image.h" -#include "io/resource_loader.h" -#include "list.h" -#include "os/file_access.h" -#include "ustring.h" +#include "core/image.h" +#include "core/io/resource_loader.h" +#include "core/list.h" +#include "core/os/file_access.h" +#include "core/ustring.h" /** @author Juan Linietsky <reduzio@gmail.com> @@ -70,20 +70,21 @@ public: class ImageLoader { - enum { - MAX_LOADERS = 8 - }; + static Vector<ImageFormatLoader *> loader; friend class ResourceFormatLoaderImage; - static ImageFormatLoader *loader[MAX_LOADERS]; - static int loader_count; protected: public: static Error load_image(String p_file, Ref<Image> p_image, FileAccess *p_custom = NULL, bool p_force_linear = false, float p_scale = 1.0); static void get_recognized_extensions(List<String> *p_extensions); - static bool recognize(const String &p_extension); + static ImageFormatLoader *recognize(const String &p_extension); static void add_image_format_loader(ImageFormatLoader *p_loader); + static void remove_image_format_loader(ImageFormatLoader *p_loader); + + static const Vector<ImageFormatLoader *> &get_image_format_loaders(); + + static void cleanup(); }; class ResourceFormatLoaderImage : public ResourceFormatLoader { diff --git a/core/io/ip.cpp b/core/io/ip.cpp index 66bd96df4f..f56e850796 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -29,9 +29,10 @@ /*************************************************************************/ #include "ip.h" -#include "hash_map.h" -#include "os/semaphore.h" -#include "os/thread.h" + +#include "core/hash_map.h" +#include "core/os/semaphore.h" +#include "core/os/thread.h" VARIANT_ENUM_CAST(IP::ResolverStatus); @@ -117,7 +118,7 @@ IP_Address IP::resolve_hostname(const String &p_hostname, IP::Type p_type) { resolver->mutex->lock(); String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type); - if (resolver->cache.has(key)) { + if (resolver->cache.has(key) && resolver->cache[key].is_valid()) { IP_Address res = resolver->cache[key]; resolver->mutex->unlock(); return res; @@ -144,7 +145,7 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String &p_hostname, IP::Typ String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type); resolver->queue[id].hostname = p_hostname; resolver->queue[id].type = p_type; - if (resolver->cache.has(key)) { + if (resolver->cache.has(key) && resolver->cache[key].is_valid()) { resolver->queue[id].response = resolver->cache[key]; resolver->queue[id].status = IP::RESOLVER_STATUS_DONE; } else { diff --git a/core/io/ip.h b/core/io/ip.h index d55b05b6fe..967f04a4bd 100644 --- a/core/io/ip.h +++ b/core/io/ip.h @@ -31,8 +31,8 @@ #ifndef IP_H #define IP_H -#include "io/ip_address.h" -#include "os/os.h" +#include "core/io/ip_address.h" +#include "core/os/os.h" struct _IP_ResolverPrivate; diff --git a/core/io/ip_address.cpp b/core/io/ip_address.cpp index 6d979d10eb..194d1af6bf 100644 --- a/core/io/ip_address.cpp +++ b/core/io/ip_address.cpp @@ -184,7 +184,7 @@ bool IP_Address::is_ipv4() const { } const uint8_t *IP_Address::get_ipv4() const { - ERR_FAIL_COND_V(!is_ipv4(), 0); + ERR_FAIL_COND_V(!is_ipv4(), &(field8[12])); // Not the correct IPv4 (it's an IPv6), but we don't want to return a null pointer risking an engine crash. return &(field8[12]); } diff --git a/core/io/ip_address.h b/core/io/ip_address.h index d7b031b960..607ce81ef4 100644 --- a/core/io/ip_address.h +++ b/core/io/ip_address.h @@ -31,7 +31,7 @@ #ifndef IP_ADDRESS_H #define IP_ADDRESS_H -#include "ustring.h" +#include "core/ustring.h" struct IP_Address { diff --git a/core/io/json.cpp b/core/io/json.cpp index 7b2c5a62df..26e18828e5 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "json.h" -#include "print_string.h" + +#include "core/print_string.h" const char *JSON::tk_name[TK_MAX] = { "'{'", diff --git a/core/io/json.h b/core/io/json.h index 9c12423798..af6c463331 100644 --- a/core/io/json.h +++ b/core/io/json.h @@ -31,7 +31,7 @@ #ifndef JSON_H #define JSON_H -#include "variant.h" +#include "core/variant.h" class JSON { diff --git a/core/io/logger.cpp b/core/io/logger.cpp index 786bec461b..01755c8ee9 100644 --- a/core/io/logger.cpp +++ b/core/io/logger.cpp @@ -30,9 +30,9 @@ #include "logger.h" -#include "os/dir_access.h" -#include "os/os.h" -#include "print_string.h" +#include "core/os/dir_access.h" +#include "core/os/os.h" +#include "core/print_string.h" // va_copy was defined in the C99, but not in C++ standards before C++11. // When you compile C++ without --std=c++<XX> option, compilers still define @@ -45,6 +45,10 @@ #endif #endif +#if defined(MINGW_ENABLED) || defined(_MSC_VER) +#define sprintf sprintf_s +#endif + bool Logger::should_log(bool p_err) { return (!p_err || _print_error_enabled) && (p_err || _print_line_enabled); } diff --git a/core/io/logger.h b/core/io/logger.h index 631e7a0589..d32b43b030 100644 --- a/core/io/logger.h +++ b/core/io/logger.h @@ -31,9 +31,10 @@ #ifndef LOGGER_H #define LOGGER_H -#include "os/file_access.h" -#include "ustring.h" -#include "vector.h" +#include "core/os/file_access.h" +#include "core/ustring.h" +#include "core/vector.h" + #include <stdarg.h> class Logger { @@ -106,4 +107,4 @@ public: virtual ~CompositeLogger(); }; -#endif
\ No newline at end of file +#endif diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index e97df0c261..6338cee39d 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -29,9 +29,11 @@ /*************************************************************************/ #include "marshalls.h" -#include "os/keyboard.h" -#include "print_string.h" -#include "reference.h" + +#include "core/os/keyboard.h" +#include "core/print_string.h" +#include "core/reference.h" + #include <limits.h> #include <stdio.h> @@ -805,7 +807,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo case Variant::INT: { int64_t val = p_variant; - if (val > 0x7FFFFFFF || val < -0x80000000) { + if (val > (int64_t)INT_MAX || val < (int64_t)INT_MIN) { flags |= ENCODE_FLAG_64; } } break; @@ -822,6 +824,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo flags |= ENCODE_FLAG_OBJECT_AS_ID; } } break; + default: {} // nothing to do at this stage } if (buf) { @@ -847,17 +850,16 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::INT: { - int64_t val = p_variant; - if (val > 0x7FFFFFFF || val < -0x80000000) { + if (flags & ENCODE_FLAG_64) { //64 bits if (buf) { - encode_uint64(val, buf); + encode_uint64(p_variant.operator int64_t(), buf); } r_len += 8; } else { if (buf) { - encode_uint32(int32_t(val), buf); + encode_uint32(p_variant.operator int32_t(), buf); } r_len += 4; @@ -865,9 +867,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::REAL: { - double d = p_variant; - float f = d; - if (double(f) != d) { + if (flags & ENCODE_FLAG_64) { if (buf) { encode_double(p_variant.operator double(), buf); } diff --git a/core/io/marshalls.h b/core/io/marshalls.h index 381e4e3d20..1284520945 100644 --- a/core/io/marshalls.h +++ b/core/io/marshalls.h @@ -31,10 +31,10 @@ #ifndef MARSHALLS_H #define MARSHALLS_H -#include "typedefs.h" +#include "core/reference.h" +#include "core/typedefs.h" +#include "core/variant.h" -#include "reference.h" -#include "variant.h" /** * Miscellaneous helpers for marshalling data types, and encoding * in an endian independent way diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp index 8e67f1c97a..b3f0a76a80 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -28,7 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/io/multiplayer_api.h" +#include "multiplayer_api.h" + #include "core/io/marshalls.h" #include "scene/main/node.h" @@ -37,24 +38,26 @@ _FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_mas switch (mode) { case MultiplayerAPI::RPC_MODE_DISABLED: { - //do nothing + // Do nothing. } break; case MultiplayerAPI::RPC_MODE_REMOTE: { - //do nothing also, no need to call local + // Do nothing also. Remote cannot produce a local call. } break; + case MultiplayerAPI::RPC_MODE_MASTERSYNC: { + if (is_master) + r_skip_rpc = true; // I am the master, so skip remote call. + } // Do not break, fall over to other sync. case MultiplayerAPI::RPC_MODE_REMOTESYNC: - case MultiplayerAPI::RPC_MODE_MASTERSYNC: - case MultiplayerAPI::RPC_MODE_SLAVESYNC: - case MultiplayerAPI::RPC_MODE_SYNC: { - //call it, sync always results in call + case MultiplayerAPI::RPC_MODE_PUPPETSYNC: { + // Call it, sync always results in a local call. return true; } break; case MultiplayerAPI::RPC_MODE_MASTER: { if (is_master) - r_skip_rpc = true; //no other master so.. + r_skip_rpc = true; // I am the master, so skip remote call. return is_master; } break; - case MultiplayerAPI::RPC_MODE_SLAVE: { + case MultiplayerAPI::RPC_MODE_PUPPET: { return !is_master; } break; } @@ -67,19 +70,16 @@ _FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, i case MultiplayerAPI::RPC_MODE_DISABLED: { return false; } break; - case MultiplayerAPI::RPC_MODE_REMOTE: { - return true; - } break; - case MultiplayerAPI::RPC_MODE_REMOTESYNC: - case MultiplayerAPI::RPC_MODE_SYNC: { + case MultiplayerAPI::RPC_MODE_REMOTE: + case MultiplayerAPI::RPC_MODE_REMOTESYNC: { return true; } break; case MultiplayerAPI::RPC_MODE_MASTERSYNC: case MultiplayerAPI::RPC_MODE_MASTER: { return p_node->is_network_master(); } break; - case MultiplayerAPI::RPC_MODE_SLAVESYNC: - case MultiplayerAPI::RPC_MODE_SLAVE: { + case MultiplayerAPI::RPC_MODE_PUPPETSYNC: + case MultiplayerAPI::RPC_MODE_PUPPET: { return !p_node->is_network_master() && p_remote_id == p_node->get_network_master(); } break; } @@ -94,7 +94,7 @@ void MultiplayerAPI::poll() { network_peer->poll(); - if (!network_peer.is_valid()) //it's possible that polling might have resulted in a disconnection, so check here + if (!network_peer.is_valid()) // It's possible that polling might have resulted in a disconnection, so check here. return; while (network_peer->get_available_packet_count()) { @@ -113,7 +113,7 @@ void MultiplayerAPI::poll() { rpc_sender_id = 0; if (!network_peer.is_valid()) { - break; //it's also possible that a packet or RPC caused a disconnection, so also check here + break; // It's also possible that a packet or RPC caused a disconnection, so also check here. } } } @@ -160,7 +160,9 @@ Ref<NetworkedMultiplayerPeer> MultiplayerAPI::get_network_peer() const { void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) { + ERR_EXPLAIN("Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it"); ERR_FAIL_COND(root_node == NULL); + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_packet_len < 1); uint8_t packet_type = p_packet[0]; @@ -180,13 +182,15 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ case NETWORK_COMMAND_REMOTE_CALL: case NETWORK_COMMAND_REMOTE_SET: { + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_packet_len < 6); Node *node = _process_get_node(p_from, p_packet, p_packet_len); + ERR_EXPLAIN("Invalid packet received. Requested node was not found."); ERR_FAIL_COND(node == NULL); - //detect cstring end + // Detect cstring end. int len_end = 5; for (; len_end < p_packet_len; len_end++) { if (p_packet[len_end] == 0) { @@ -194,6 +198,7 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ } } + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(len_end >= p_packet_len); StringName name = String::utf8((const char *)&p_packet[5]); @@ -222,9 +227,11 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int Node *node = NULL; if (target & 0x80000000) { - //use full path (not cached yet) + // Use full path (not cached yet). int ofs = target & 0x7FFFFFFF; + + ERR_EXPLAIN("Invalid packet received. Size smaller than declared."); ERR_FAIL_COND_V(ofs >= p_packet_len, NULL); String paths; @@ -237,17 +244,19 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int if (!node) ERR_PRINTS("Failed to get path from RPC: " + String(np)); } else { - //use cached path + // Use cached path. int id = target; Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from); + ERR_EXPLAIN("Invalid packet received. Requests invalid peer cache."); ERR_FAIL_COND_V(!E, NULL); Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(id); + ERR_EXPLAIN("Invalid packet received. Unabled to find requested cached node."); ERR_FAIL_COND_V(!F, NULL); PathGetCache::NodeInfo *ni = &F->get(); - //do proper caching later + // Do proper caching later. node = root_node->get_node(ni->path); if (!node) @@ -258,9 +267,10 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_offset >= p_packet_len); - // Check that remote can call the RPC on this node + // Check that remote can call the RPC on this node. RPCMode rpc_mode = RPC_MODE_DISABLED; const Map<StringName, RPCMode>::Element *E = p_node->get_node_rpc_mode(p_name); if (E) { @@ -268,6 +278,8 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ } else if (p_node->get_script_instance()) { rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_name); } + + ERR_EXPLAIN("RPC '" + String(p_name) + "' is not allowed from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + "."); ERR_FAIL_COND(!_can_call_mode(p_node, rpc_mode, p_from)); int argc = p_packet[p_offset]; @@ -280,11 +292,14 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ for (int i = 0; i < argc; i++) { + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_offset >= p_packet_len); + int vlen; Error err = decode_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen); + ERR_EXPLAIN("Invalid packet received. Unable to decode RPC argument."); ERR_FAIL_COND(err != OK); - //args[i]=p_packet[3+i]; + argp.write[i] = &args[i]; p_offset += vlen; } @@ -301,9 +316,10 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_offset >= p_packet_len); - // Check that remote can call the RSET on this node + // Check that remote can call the RSET on this node. RPCMode rset_mode = RPC_MODE_DISABLED; const Map<StringName, RPCMode>::Element *E = p_node->get_node_rset_mode(p_name); if (E) { @@ -311,10 +327,15 @@ void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p } else if (p_node->get_script_instance()) { rset_mode = p_node->get_script_instance()->get_rset_mode(p_name); } + + ERR_EXPLAIN("RSET '" + String(p_name) + "' is not allowed from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + "."); ERR_FAIL_COND(!_can_call_mode(p_node, rset_mode, p_from)); Variant value; - decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset); + Error err = decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset); + + ERR_EXPLAIN("Invalid packet received. Unable to decode RSET value."); + ERR_FAIL_COND(err != OK); bool valid; @@ -327,6 +348,7 @@ void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) { + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_packet_len < 5); int id = decode_uint32(&p_packet[1]); @@ -345,9 +367,7 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, path_get_cache[p_from].nodes[id] = ni; - //send ack - - //encode path + // Encode path to send ack. CharString pname = String(path).utf8(); int len = encode_cstring(pname.get_data(), NULL); @@ -364,6 +384,7 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) { + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_packet_len < 2); String paths; @@ -372,31 +393,33 @@ void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, NodePath path = paths; PathSentCache *psc = path_send_cache.getptr(path); + ERR_EXPLAIN("Invalid packet received. Tries to confirm a path which was not found in cache."); ERR_FAIL_COND(!psc); Map<int, bool>::Element *E = psc->confirmed_peers.find(p_from); + ERR_EXPLAIN("Invalid packet received. Source peer was not found in cache for the given path."); ERR_FAIL_COND(!E); E->get() = true; } bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int p_target) { bool has_all_peers = true; - List<int> peers_to_add; //if one is missing, take note to add it + List<int> peers_to_add; // If one is missing, take note to add it. for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { if (p_target < 0 && E->get() == -p_target) - continue; //continue, excluded + continue; // Continue, excluded. if (p_target > 0 && E->get() != p_target) - continue; //continue, not for this peer + continue; // Continue, not for this peer. Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); - if (!F || F->get() == false) { - //path was not cached, or was cached but is unconfirmed + if (!F || !F->get()) { + // Path was not cached, or was cached but is unconfirmed. if (!F) { - //not cached at all, take note + // Not cached at all, take note. peers_to_add.push_back(E->get()); } @@ -404,11 +427,11 @@ bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int } } - //those that need to be added, send a message for this + // Those that need to be added, send a message for this. for (List<int>::Element *E = peers_to_add.front(); E; E = E->next()) { - //encode function name + // Encode function name. CharString pname = String(p_path).utf8(); int len = encode_cstring(pname.get_data(), NULL); @@ -419,11 +442,11 @@ bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int encode_uint32(psc->id, &packet.write[1]); encode_cstring(pname.get_data(), &packet.write[5]); - network_peer->set_target_peer(E->get()); //to all of you + network_peer->set_target_peer(E->get()); // To all of you. network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); network_peer->put_packet(packet.ptr(), packet.size()); - psc->confirmed_peers.insert(E->get(), false); //insert into confirmed, but as false since it was not confirmed + psc->confirmed_peers.insert(E->get(), false); // Insert into confirmed, but as false since it was not confirmed. } return has_all_peers; @@ -462,35 +485,36 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p } NodePath from_path = (root_node->get_path()).rel_path_to(p_from->get_path()); + ERR_EXPLAIN("Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!"); ERR_FAIL_COND(from_path.is_empty()); - //see if the path is cached + // See if the path is cached. PathSentCache *psc = path_send_cache.getptr(from_path); if (!psc) { - //path is not cached, create + // Path is not cached, create. path_send_cache[from_path] = PathSentCache(); psc = path_send_cache.getptr(from_path); psc->id = last_send_cache_id++; } - //create base packet, lots of hardcode because it must be tight + // Create base packet, lots of hardcode because it must be tight. int ofs = 0; #define MAKE_ROOM(m_amount) \ if (packet_cache.size() < m_amount) packet_cache.resize(m_amount); - //encode type + // Encode type. MAKE_ROOM(1); packet_cache.write[0] = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL; ofs += 1; - //encode ID + // Encode ID. MAKE_ROOM(ofs + 4); encode_uint32(psc->id, &(packet_cache.write[ofs])); ofs += 4; - //encode function name + // Encode function name. CharString name = String(p_name).utf8(); int len = encode_cstring(name.get_data(), NULL); MAKE_ROOM(ofs + len); @@ -498,20 +522,22 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p ofs += len; if (p_set) { - //set argument + // Set argument. Error err = encode_variant(*p_arg[0], NULL, len); + ERR_EXPLAIN("Unable to encode RSET value. THIS IS LIKELY A BUG IN THE ENGINE!"); ERR_FAIL_COND(err != OK); MAKE_ROOM(ofs + len); encode_variant(*p_arg[0], &(packet_cache.write[ofs]), len); ofs += len; } else { - //call arguments + // Call arguments. MAKE_ROOM(ofs + 1); packet_cache.write[ofs] = p_argcount; ofs += 1; for (int i = 0; i < p_argcount; i++) { Error err = encode_variant(*p_arg[i], NULL, len); + ERR_EXPLAIN("Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!"); ERR_FAIL_COND(err != OK); MAKE_ROOM(ofs + len); encode_variant(*p_arg[i], &(packet_cache.write[ofs]), len); @@ -519,21 +545,21 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p } } - //see if all peers have cached path (is so, call can be fast) + // See if all peers have cached path (is so, call can be fast). bool has_all_peers = _send_confirm_path(from_path, psc, p_to); - //take chance and set transfer mode, since all send methods will use it + // Take chance and set transfer mode, since all send methods will use it. network_peer->set_transfer_mode(p_unreliable ? NetworkedMultiplayerPeer::TRANSFER_MODE_UNRELIABLE : NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); if (has_all_peers) { - //they all have verified paths, so send fast - network_peer->set_target_peer(p_to); //to all of you - network_peer->put_packet(packet_cache.ptr(), ofs); //a message with love + // They all have verified paths, so send fast. + network_peer->set_target_peer(p_to); // To all of you. + network_peer->put_packet(packet_cache.ptr(), ofs); // A message with love. } else { - //not all verified path, so send one by one + // Not all verified path, so send one by one. - //apend path at the end, since we will need it for some packets + // Append path at the end, since we will need it for some packets. CharString pname = String(from_path).utf8(); int path_len = encode_cstring(pname.get_data(), NULL); MAKE_ROOM(ofs + path_len); @@ -542,23 +568,23 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { if (p_to < 0 && E->get() == -p_to) - continue; //continue, excluded + continue; // Continue, excluded. if (p_to > 0 && E->get() != p_to) - continue; //continue, not for this peer + continue; // Continue, not for this peer. Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); - ERR_CONTINUE(!F); //should never happen + ERR_CONTINUE(!F); // Should never happen. - network_peer->set_target_peer(E->get()); //to this one specifically + network_peer->set_target_peer(E->get()); // To this one specifically. - if (F->get() == true) { - //this one confirmed path, so use id + if (F->get()) { + // This one confirmed path, so use id. encode_uint32(psc->id, &(packet_cache.write[1])); network_peer->put_packet(packet_cache.ptr(), ofs); } else { - //this one did not confirm path yet, so use entire path (sorry!) - encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); //offset to path and flag + // This one did not confirm path yet, so use entire path (sorry!). + encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); // Offset to path and flag. network_peer->put_packet(packet_cache.ptr(), ofs + path_len); } } @@ -573,7 +599,7 @@ void MultiplayerAPI::_add_peer(int p_id) { void MultiplayerAPI::_del_peer(int p_id) { connected_peers.erase(p_id); - path_get_cache.erase(p_id); //I no longer need your cache, sorry + path_get_cache.erase(p_id); // I no longer need your cache, sorry. emit_signal("network_peer_disconnected", p_id); } @@ -594,8 +620,12 @@ void MultiplayerAPI::_server_disconnected() { void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) { - ERR_FAIL_COND(!p_node->is_inside_tree()); + ERR_EXPLAIN("Trying to call an RPC while no network peer is active."); ERR_FAIL_COND(!network_peer.is_valid()); + ERR_EXPLAIN("Trying to call an RPC on a node which is not inside SceneTree."); + ERR_FAIL_COND(!p_node->is_inside_tree()); + ERR_EXPLAIN("Trying to call an RPC via a network peer which is not connected."); + ERR_FAIL_COND(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED); int node_id = network_peer->get_unique_id(); bool skip_rpc = false; @@ -604,7 +634,7 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const bool is_master = p_node->is_network_master(); if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { - //check that send mode can use local call + // Check that send mode can use local call. const Map<StringName, RPCMode>::Element *E = p_node->get_node_rpc_mode(p_method); if (E) { @@ -612,9 +642,9 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const } if (call_local_native) { - // done below + // Done below. } else if (p_node->get_script_instance()) { - //attempt with script + // Attempt with script. RPCMode rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method); call_local_script = _should_call_local(rpc_mode, is_master, skip_rpc); } @@ -650,15 +680,19 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value) { - ERR_FAIL_COND(!p_node->is_inside_tree()); + ERR_EXPLAIN("Trying to RSET while no network peer is active."); ERR_FAIL_COND(!network_peer.is_valid()); + ERR_EXPLAIN("Trying to RSET on a node which is not inside SceneTree."); + ERR_FAIL_COND(!p_node->is_inside_tree()); + ERR_EXPLAIN("Trying to send an RSET via a network peer which is not connected."); + ERR_FAIL_COND(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED); int node_id = network_peer->get_unique_id(); bool is_master = p_node->is_network_master(); bool skip_rset = false; if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { - //check that send mode can use local call + // Check that send mode can use local call. bool set_local = false; @@ -678,7 +712,7 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const return; } } else if (p_node->get_script_instance()) { - //attempt with script + // Attempt with script. RPCMode rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property); set_local = _should_call_local(rpc_mode, is_master, skip_rset); @@ -706,8 +740,11 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const Error MultiplayerAPI::send_bytes(PoolVector<uint8_t> p_data, int p_to, NetworkedMultiplayerPeer::TransferMode p_mode) { + ERR_EXPLAIN("Trying to send an empty raw packet."); ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA); + ERR_EXPLAIN("Trying to send a raw packet while no network peer is active."); ERR_FAIL_COND_V(!network_peer.is_valid(), ERR_UNCONFIGURED); + ERR_EXPLAIN("Trying to send a raw packet via a network peer which is not connected."); ERR_FAIL_COND_V(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED); MAKE_ROOM(p_data.size() + 1); @@ -723,6 +760,7 @@ Error MultiplayerAPI::send_bytes(PoolVector<uint8_t> p_data, int p_to, Networked void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) { + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_packet_len < 2); PoolVector<uint8_t> out; @@ -737,30 +775,36 @@ void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_pac int MultiplayerAPI::get_network_unique_id() const { + ERR_EXPLAIN("No network peer is assigned. Unable to get unique network ID."); ERR_FAIL_COND_V(!network_peer.is_valid(), 0); return network_peer->get_unique_id(); } bool MultiplayerAPI::is_network_server() const { + // XXX Maybe fail silently? Maybe should actually return true to make development of both local and online multiplayer easier? + ERR_EXPLAIN("No network peer is assigned. I can't be a server."); ERR_FAIL_COND_V(!network_peer.is_valid(), false); return network_peer->is_server(); } void MultiplayerAPI::set_refuse_new_network_connections(bool p_refuse) { + ERR_EXPLAIN("No network peer is assigned. Unable to set 'refuse_new_connections'."); ERR_FAIL_COND(!network_peer.is_valid()); network_peer->set_refuse_new_connections(p_refuse); } bool MultiplayerAPI::is_refusing_new_network_connections() const { + ERR_EXPLAIN("No network peer is assigned. Unable to get 'refuse_new_connections'."); ERR_FAIL_COND_V(!network_peer.is_valid(), false); return network_peer->is_refusing_new_connections(); } Vector<int> MultiplayerAPI::get_network_connected_peers() const { + ERR_EXPLAIN("No network peer is assigned. Assume no peers are connected."); ERR_FAIL_COND_V(!network_peer.is_valid(), Vector<int>()); Vector<int> ret; @@ -803,12 +847,13 @@ void MultiplayerAPI::_bind_methods() { BIND_ENUM_CONSTANT(RPC_MODE_DISABLED); BIND_ENUM_CONSTANT(RPC_MODE_REMOTE); - BIND_ENUM_CONSTANT(RPC_MODE_SYNC); BIND_ENUM_CONSTANT(RPC_MODE_MASTER); - BIND_ENUM_CONSTANT(RPC_MODE_SLAVE); + BIND_ENUM_CONSTANT(RPC_MODE_PUPPET); + BIND_ENUM_CONSTANT(RPC_MODE_SLAVE); // Deprecated. BIND_ENUM_CONSTANT(RPC_MODE_REMOTESYNC); + BIND_ENUM_CONSTANT(RPC_MODE_SYNC); // Deprecated. BIND_ENUM_CONSTANT(RPC_MODE_MASTERSYNC); - BIND_ENUM_CONSTANT(RPC_MODE_SLAVESYNC); + BIND_ENUM_CONSTANT(RPC_MODE_PUPPETSYNC); } MultiplayerAPI::MultiplayerAPI() { diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h index e47b1830e8..c86e76e91a 100644 --- a/core/io/multiplayer_api.h +++ b/core/io/multiplayer_api.h @@ -91,12 +91,13 @@ public: RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default) RPC_MODE_REMOTE, // Using rpc() on it will call method / set property in all remote peers - RPC_MODE_SYNC, // Using rpc() on it will call method / set property in all remote peers and locally RPC_MODE_MASTER, // Using rpc() on it will call method on wherever the master is, be it local or remote - RPC_MODE_SLAVE, // Using rpc() on it will call method for all slaves - RPC_MODE_REMOTESYNC, // Same as RPC_MODE_SYNC, compatibility + RPC_MODE_PUPPET, // Using rpc() on it will call method for all puppets + RPC_MODE_SLAVE = RPC_MODE_PUPPET, // Deprecated, same as puppet + RPC_MODE_REMOTESYNC, // Using rpc() on it will call method / set property in all remote peers and locally + RPC_MODE_SYNC = RPC_MODE_REMOTESYNC, // Deprecated. Same as RPC_MODE_REMOTESYNC RPC_MODE_MASTERSYNC, // Using rpc() on it will call method / set property in the master peer and locally - RPC_MODE_SLAVESYNC, // Using rpc() on it will call method / set property in all slave peers and locally + RPC_MODE_PUPPETSYNC, // Using rpc() on it will call method / set property in all puppets peers and locally }; void poll(); diff --git a/core/helper/value_evaluator.h b/core/io/net_socket.cpp index 39177a7820..10bcf62eda 100644 --- a/core/helper/value_evaluator.h +++ b/core/io/net_socket.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* value_evaluator.h */ +/* net_socket.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,19 +28,15 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef VALUE_EVALUATOR_H -#define VALUE_EVALUATOR_H +#include "net_socket.h" -#include "core/object.h" +NetSocket *(*NetSocket::_create)() = NULL; -class ValueEvaluator : public Object { +NetSocket *NetSocket::create() { - GDCLASS(ValueEvaluator, Object); + if (_create) + return _create(); -public: - virtual double eval(const String &p_text) { - return p_text.to_double(); - } -}; - -#endif // VALUE_EVALUATOR_H + ERR_PRINT("Unable to create network socket, platform not supported"); + return NULL; +} diff --git a/core/io/net_socket.h b/core/io/net_socket.h new file mode 100644 index 0000000000..0665bec9fd --- /dev/null +++ b/core/io/net_socket.h @@ -0,0 +1,79 @@ +/*************************************************************************/ +/* net_socket.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef NET_SOCKET_H +#define NET_SOCKET_H + +#include "core/io/ip.h" +#include "core/reference.h" + +class NetSocket : public Reference { + +protected: + static NetSocket *(*_create)(); + +public: + static NetSocket *create(); + + enum PollType { + POLL_TYPE_IN, + POLL_TYPE_OUT, + POLL_TYPE_IN_OUT + }; + + enum Type { + TYPE_NONE, + TYPE_TCP, + TYPE_UDP, + }; + + virtual Error open(Type p_type, IP::Type &ip_type) = 0; + virtual void close() = 0; + virtual Error bind(IP_Address p_addr, uint16_t p_port) = 0; + virtual Error listen(int p_max_pending) = 0; + virtual Error connect_to_host(IP_Address p_addr, uint16_t p_port) = 0; + virtual Error poll(PollType p_type, int timeout) const = 0; + virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read) = 0; + virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port) = 0; + virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent) = 0; + virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP_Address p_ip, uint16_t p_port) = 0; + virtual Ref<NetSocket> accept(IP_Address &r_ip, uint16_t &r_port) = 0; + + virtual bool is_open() const = 0; + virtual int get_available_bytes() const = 0; + + virtual void set_broadcasting_enabled(bool p_enabled) = 0; + virtual void set_blocking_enabled(bool p_enabled) = 0; + virtual void set_ipv6_only_enabled(bool p_enabled) = 0; + virtual void set_tcp_no_delay_enabled(bool p_enabled) = 0; + virtual void set_reuse_address_enabled(bool p_enabled) = 0; +}; + +#endif // NET_SOCKET_H diff --git a/core/io/networked_multiplayer_peer.h b/core/io/networked_multiplayer_peer.h index 66089c27b9..8e83c528a0 100644 --- a/core/io/networked_multiplayer_peer.h +++ b/core/io/networked_multiplayer_peer.h @@ -31,7 +31,7 @@ #ifndef NETWORKED_MULTIPLAYER_PEER_H #define NETWORKED_MULTIPLAYER_PEER_H -#include "io/packet_peer.h" +#include "core/io/packet_peer.h" class NetworkedMultiplayerPeer : public PacketPeer { diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index dc4997dfc2..b6dd4eaf6f 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -30,8 +30,9 @@ #include "packet_peer.h" -#include "io/marshalls.h" -#include "project_settings.h" +#include "core/io/marshalls.h" +#include "core/project_settings.h" + /* helpers / binders */ PacketPeer::PacketPeer() { diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h index b10152e96b..a6559df460 100644 --- a/core/io/packet_peer.h +++ b/core/io/packet_peer.h @@ -31,9 +31,10 @@ #ifndef PACKET_PEER_H #define PACKET_PEER_H -#include "io/stream_peer.h" -#include "object.h" -#include "ring_buffer.h" +#include "core/io/stream_peer.h" +#include "core/object.h" +#include "core/ring_buffer.h" + class PacketPeer : public Reference { GDCLASS(PacketPeer, Reference); diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp index bfbea15582..d33ba6f855 100644 --- a/core/io/packet_peer_udp.cpp +++ b/core/io/packet_peer_udp.cpp @@ -29,9 +29,8 @@ /*************************************************************************/ #include "packet_peer_udp.h" -#include "io/ip.h" -PacketPeerUDP *(*PacketPeerUDP::_create)() = NULL; +#include "core/io/ip.h" void PacketPeerUDP::set_blocking_mode(bool p_enable) { @@ -58,6 +57,177 @@ Error PacketPeerUDP::_set_dest_address(const String &p_address, int p_port) { return OK; } +int PacketPeerUDP::get_available_packet_count() const { + + // TODO we should deprecate this, and expose poll instead! + Error err = const_cast<PacketPeerUDP *>(this)->_poll(); + if (err != OK) + return -1; + + return queue_count; +} + +Error PacketPeerUDP::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + + Error err = _poll(); + if (err != OK) + return err; + if (queue_count == 0) + return ERR_UNAVAILABLE; + + uint32_t size = 0; + uint8_t ipv6[16]; + rb.read(ipv6, 16, true); + packet_ip.set_ipv6(ipv6); + rb.read((uint8_t *)&packet_port, 4, true); + rb.read((uint8_t *)&size, 4, true); + rb.read(packet_buffer, size, true); + --queue_count; + *r_buffer = packet_buffer; + r_buffer_size = size; + return OK; +} + +Error PacketPeerUDP::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + ERR_FAIL_COND_V(!peer_addr.is_valid(), ERR_UNCONFIGURED); + + Error err; + int sent = -1; + + if (!_sock->is_open()) { + IP::Type ip_type = peer_addr.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + err = _sock->open(NetSocket::TYPE_UDP, ip_type); + ERR_FAIL_COND_V(err != OK, err); + _sock->set_blocking_enabled(false); + } + + do { + err = _sock->sendto(p_buffer, p_buffer_size, sent, peer_addr, peer_port); + if (err != OK) { + if (err != ERR_BUSY) + return FAILED; + else if (!blocking) + return ERR_BUSY; + // Keep trying to send full packet + continue; + } + return OK; + + } while (sent != p_buffer_size); + + return OK; +} + +int PacketPeerUDP::get_max_packet_size() const { + + return 512; // uhm maybe not +} + +Error PacketPeerUDP::listen(int p_port, const IP_Address &p_bind_address, int p_recv_buffer_size) { + + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER); + + Error err; + IP::Type ip_type = IP::TYPE_ANY; + + if (p_bind_address.is_valid()) + ip_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + + err = _sock->open(NetSocket::TYPE_UDP, ip_type); + + if (err != OK) + return ERR_CANT_CREATE; + + _sock->set_blocking_enabled(false); + _sock->set_reuse_address_enabled(true); + err = _sock->bind(p_bind_address, p_port); + + if (err != OK) { + _sock->close(); + return err; + } + rb.resize(nearest_shift(p_recv_buffer_size)); + return OK; +} + +void PacketPeerUDP::close() { + + if (_sock.is_valid()) + _sock->close(); + rb.resize(16); + queue_count = 0; +} + +Error PacketPeerUDP::wait() { + + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + return _sock->poll(NetSocket::POLL_TYPE_IN, -1); +} + +Error PacketPeerUDP::_poll() { + + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + + if (!_sock->is_open()) { + return FAILED; + } + + Error err; + int read; + IP_Address ip; + uint16_t port; + + while (true) { + err = _sock->recvfrom(recv_buffer, sizeof(recv_buffer), read, ip, port); + + if (err != OK) { + if (err == ERR_BUSY) + break; + return FAILED; + } + + if (rb.space_left() < read + 24) { +#ifdef TOOLS_ENABLED + WARN_PRINTS("Buffer full, dropping packets!"); +#endif + continue; + } + + uint32_t port32 = port; + rb.write(ip.get_ipv6(), 16); + rb.write((uint8_t *)&port32, 4); + rb.write((uint8_t *)&read, 4); + rb.write(recv_buffer, read); + ++queue_count; + } + + return OK; +} +bool PacketPeerUDP::is_listening() const { + + return _sock.is_valid() && _sock->is_open(); +} + +IP_Address PacketPeerUDP::get_packet_address() const { + + return packet_ip; +} + +int PacketPeerUDP::get_packet_port() const { + + return packet_port; +} + +void PacketPeerUDP::set_dest_address(const IP_Address &p_address, int p_port) { + + peer_addr = p_address; + peer_port = p_port; +} + void PacketPeerUDP::_bind_methods() { ClassDB::bind_method(D_METHOD("listen", "port", "bind_address", "recv_buf_size"), &PacketPeerUDP::listen, DEFVAL("*"), DEFVAL(65536)); @@ -65,26 +235,21 @@ void PacketPeerUDP::_bind_methods() { ClassDB::bind_method(D_METHOD("wait"), &PacketPeerUDP::wait); ClassDB::bind_method(D_METHOD("is_listening"), &PacketPeerUDP::is_listening); ClassDB::bind_method(D_METHOD("get_packet_ip"), &PacketPeerUDP::_get_packet_ip); - //ClassDB::bind_method(D_METHOD("get_packet_address"),&PacketPeerUDP::_get_packet_address); ClassDB::bind_method(D_METHOD("get_packet_port"), &PacketPeerUDP::get_packet_port); ClassDB::bind_method(D_METHOD("set_dest_address", "host", "port"), &PacketPeerUDP::_set_dest_address); } -Ref<PacketPeerUDP> PacketPeerUDP::create_ref() { - - if (!_create) - return Ref<PacketPeerUDP>(); - return Ref<PacketPeerUDP>(_create()); -} - -PacketPeerUDP *PacketPeerUDP::create() { +PacketPeerUDP::PacketPeerUDP() { - if (!_create) - return NULL; - return _create(); + _sock = Ref<NetSocket>(NetSocket::create()); + blocking = true; + packet_port = 0; + queue_count = 0; + peer_port = 0; + rb.resize(16); } -PacketPeerUDP::PacketPeerUDP() { +PacketPeerUDP::~PacketPeerUDP() { - blocking = true; + close(); } diff --git a/core/io/packet_peer_udp.h b/core/io/packet_peer_udp.h index 035f4ad1c9..4366b0eb82 100644 --- a/core/io/packet_peer_udp.h +++ b/core/io/packet_peer_udp.h @@ -31,37 +31,55 @@ #ifndef PACKET_PEER_UDP_H #define PACKET_PEER_UDP_H -#include "io/ip.h" -#include "io/packet_peer.h" +#include "core/io/ip.h" +#include "core/io/net_socket.h" +#include "core/io/packet_peer.h" class PacketPeerUDP : public PacketPeer { GDCLASS(PacketPeerUDP, PacketPeer); protected: + enum { + PACKET_BUFFER_SIZE = 65536 + }; + + RingBuffer<uint8_t> rb; + uint8_t recv_buffer[PACKET_BUFFER_SIZE]; + uint8_t packet_buffer[PACKET_BUFFER_SIZE]; + IP_Address packet_ip; + int packet_port; + int queue_count; + + IP_Address peer_addr; + int peer_port; bool blocking; + Ref<NetSocket> _sock; - static PacketPeerUDP *(*_create)(); static void _bind_methods(); String _get_packet_ip() const; Error _set_dest_address(const String &p_address, int p_port); + Error _poll(); public: void set_blocking_mode(bool p_enable); - virtual Error listen(int p_port, const IP_Address &p_bind_address = IP_Address("*"), int p_recv_buffer_size = 65536) = 0; - virtual void close() = 0; - virtual Error wait() = 0; - virtual bool is_listening() const = 0; - virtual IP_Address get_packet_address() const = 0; - virtual int get_packet_port() const = 0; - virtual void set_dest_address(const IP_Address &p_address, int p_port) = 0; + Error listen(int p_port, const IP_Address &p_bind_address = IP_Address("*"), int p_recv_buffer_size = 65536); + void close(); + Error wait(); + bool is_listening() const; + IP_Address get_packet_address() const; + int get_packet_port() const; + void set_dest_address(const IP_Address &p_address, int p_port); - static Ref<PacketPeerUDP> create_ref(); - static PacketPeerUDP *create(); + Error put_packet(const uint8_t *p_buffer, int p_buffer_size); + Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); + int get_available_packet_count() const; + int get_max_packet_size() const; PacketPeerUDP(); + ~PacketPeerUDP(); }; #endif // PACKET_PEER_UDP_H diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index 2fd73db27d..3df8c01760 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -29,8 +29,9 @@ /*************************************************************************/ #include "pck_packer.h" + #include "core/os/file_access.h" -#include "version.h" +#include "core/version.h" static uint64_t _align(uint64_t p_n, int p_alignment) { diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 02c2c6ce1a..6f3a8c3d2e 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -1309,7 +1309,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia case Variant::INT: { int64_t val = p_property; - if (val > 0x7FFFFFFF || val < -0x80000000) { + if (val > 0x7FFFFFFF || val < -(int64_t)0x80000000) { f->store_32(VARIANT_INT64); f->store_64(val); @@ -1718,7 +1718,7 @@ void ResourceFormatSaverBinaryInstance::save_unicode_string(FileAccess *f, const CharString utf8 = p_string.utf8(); if (p_bit_on_len) { - f->store_32(utf8.length() + 1 | 0x80000000); + f->store_32((utf8.length() + 1) | 0x80000000); } else { f->store_32(utf8.length() + 1); } @@ -1813,8 +1813,13 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p Property p; p.name_idx = get_string_index(F->get().name); p.value = E->get()->get(F->get().name); - if (((F->get().usage & PROPERTY_USAGE_STORE_IF_NONZERO) && p.value.is_zero()) || ((F->get().usage & PROPERTY_USAGE_STORE_IF_NONONE) && p.value.is_one())) + + Variant default_value = ClassDB::class_get_default_property_value(E->get()->get_class(), F->get().name); + + if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, p.value, default_value))) { continue; + } + p.pi = F->get(); rd.properties.push_back(p); diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h index 021f7f6a2f..513252055f 100644 --- a/core/io/resource_format_binary.h +++ b/core/io/resource_format_binary.h @@ -31,9 +31,9 @@ #ifndef RESOURCE_FORMAT_BINARY_H #define RESOURCE_FORMAT_BINARY_H -#include "io/resource_loader.h" -#include "io/resource_saver.h" -#include "os/file_access.h" +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" +#include "core/os/file_access.h" class ResourceInteractiveLoaderBinary : public ResourceInteractiveLoader { @@ -119,7 +119,6 @@ class ResourceFormatSaverBinaryInstance { bool skip_editor; bool big_endian; bool takeover_paths; - int bin_meta_idx; FileAccess *f; String magic; Set<RES> resource_set; diff --git a/core/io/resource_import.cpp b/core/io/resource_import.cpp index cfe6655504..ffea43b6bf 100644 --- a/core/io/resource_import.cpp +++ b/core/io/resource_import.cpp @@ -30,8 +30,8 @@ #include "resource_import.h" -#include "os/os.h" -#include "variant_parser.h" +#include "core/os/os.h" +#include "core/variant_parser.h" Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndType &r_path_and_type, bool *r_valid) const { diff --git a/core/io/resource_import.h b/core/io/resource_import.h index 80e0743eda..53718bd789 100644 --- a/core/io/resource_import.h +++ b/core/io/resource_import.h @@ -31,7 +31,8 @@ #ifndef RESOURCE_IMPORT_H #define RESOURCE_IMPORT_H -#include "io/resource_loader.h" +#include "core/io/resource_loader.h" + class ResourceImporter; class ResourceFormatImporter : public ResourceFormatLoader { diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index c44d2597a7..71b01aa94a 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -29,14 +29,16 @@ /*************************************************************************/ #include "resource_loader.h" -#include "io/resource_import.h" -#include "os/file_access.h" -#include "os/os.h" -#include "path_remap.h" -#include "print_string.h" -#include "project_settings.h" -#include "translation.h" -#include "variant_parser.h" + +#include "core/io/resource_import.h" +#include "core/os/file_access.h" +#include "core/os/os.h" +#include "core/path_remap.h" +#include "core/print_string.h" +#include "core/project_settings.h" +#include "core/translation.h" +#include "core/variant_parser.h" + ResourceFormatLoader *ResourceLoader::loader[MAX_LOADERS]; int ResourceLoader::loader_count = 0; @@ -123,6 +125,10 @@ Ref<ResourceInteractiveLoader> ResourceFormatLoader::load_interactive(const Stri return ril; } +bool ResourceFormatLoader::exists(const String &p_path) const { + return FileAccess::exists(p_path); //by default just check file +} + RES ResourceFormatLoader::load(const String &p_path, const String &p_original_path, Error *r_error) { String path = p_path; @@ -198,13 +204,32 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p else local_path = ProjectSettings::get_singleton()->localize_path(p_path); - if (!p_no_cache && ResourceCache::has(local_path)) { + if (!p_no_cache) { + //lock first if possible + if (ResourceCache::lock) { + ResourceCache::lock->read_lock(); + } - if (OS::get_singleton()->is_stdout_verbose()) - print_line("load resource: " + local_path + " (cached)"); - if (r_error) - *r_error = OK; - return RES(ResourceCache::get(local_path)); + //get ptr + Resource **rptr = ResourceCache::resources.getptr(local_path); + + if (rptr) { + RES res(*rptr); + //it is possible this resource was just freed in a thread. If so, this referencing will not work and resource is considered not cached + if (res.is_valid()) { + //referencing is fine + if (r_error) + *r_error = OK; + if (ResourceCache::lock) { + ResourceCache::lock->read_unlock(); + } + print_verbose("Loading resource: " + local_path + " (cached)"); + return res; + } + } + if (ResourceCache::lock) { + ResourceCache::lock->read_unlock(); + } } bool xl_remapped = false; @@ -212,9 +237,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p ERR_FAIL_COND_V(path == "", RES()); - if (OS::get_singleton()->is_stdout_verbose()) - print_line("load resource: " + path); - + print_verbose("Loading resource: " + path); RES res = _load(path, local_path, p_type_hint, p_no_cache, r_error); if (res.is_null()) { @@ -236,9 +259,43 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p } #endif + if (_loaded_callback) { + _loaded_callback(res, p_path); + } + return res; } +bool ResourceLoader::exists(const String &p_path, const String &p_type_hint) { + + String local_path; + if (p_path.is_rel_path()) + local_path = "res://" + p_path; + else + local_path = ProjectSettings::get_singleton()->localize_path(p_path); + + if (ResourceCache::has(local_path)) { + + return true; // If cached, it probably exists + } + + bool xl_remapped = false; + String path = _path_remap(local_path, &xl_remapped); + + // Try all loaders and pick the first match for the type hint + for (int i = 0; i < loader_count; i++) { + + if (!loader[i]->recognize_path(path, p_type_hint)) { + continue; + } + + if (loader[i]->exists(path)) + return true; + } + + return false; +} + Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_path, const String &p_type_hint, bool p_no_cache, Error *r_error) { if (r_error) @@ -252,9 +309,7 @@ Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_ if (!p_no_cache && ResourceCache::has(local_path)) { - if (OS::get_singleton()->is_stdout_verbose()) - print_line("load resource: " + local_path + " (cached)"); - + print_verbose("Loading resource: " + local_path + " (cached)"); Ref<Resource> res_cached = ResourceCache::get(local_path); Ref<ResourceInteractiveLoaderDefault> ril = Ref<ResourceInteractiveLoaderDefault>(memnew(ResourceInteractiveLoaderDefault)); @@ -264,14 +319,10 @@ Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_ bool xl_remapped = false; String path = _path_remap(local_path, &xl_remapped); - ERR_FAIL_COND_V(path == "", Ref<ResourceInteractiveLoader>()); - - if (OS::get_singleton()->is_stdout_verbose()) - print_line("load resource: "); + print_verbose("Loading resource: " + path); bool found = false; - for (int i = 0; i < loader_count; i++) { if (!loader[i]->recognize_path(path, p_type_hint)) @@ -588,6 +639,12 @@ void ResourceLoader::clear_path_remaps() { path_remaps.clear(); } +void ResourceLoader::set_load_callback(ResourceLoadedCallback p_callback) { + _loaded_callback = p_callback; +} + +ResourceLoadedCallback ResourceLoader::_loaded_callback = NULL; + ResourceLoadErrorNotify ResourceLoader::err_notify = NULL; void *ResourceLoader::err_notify_ud = NULL; @@ -600,3 +657,5 @@ bool ResourceLoader::timestamp_on_load = false; SelfList<Resource>::List ResourceLoader::remapped_list; HashMap<String, Vector<String> > ResourceLoader::translation_remaps; HashMap<String, String> ResourceLoader::path_remaps; + +ResourceLoaderImport ResourceLoader::import = NULL; diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index 9be82abb42..a46a00203f 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -31,7 +31,7 @@ #ifndef RESOURCE_LOADER_H #define RESOURCE_LOADER_H -#include "resource.h" +#include "core/resource.h" /** @author Juan Linietsky <reduzio@gmail.com> @@ -60,6 +60,7 @@ class ResourceFormatLoader { public: virtual Ref<ResourceInteractiveLoader> load_interactive(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual bool exists(const String &p_path) const; virtual void get_recognized_extensions(List<String> *p_extensions) const = 0; virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const; virtual bool recognize_path(const String &p_path, const String &p_for_type = String()) const; @@ -76,6 +77,9 @@ public: typedef void (*ResourceLoadErrorNotify)(void *p_ud, const String &p_text); typedef void (*DependencyErrorNotify)(void *p_ud, const String &p_loading, const String &p_which, const String &p_type); +typedef Error (*ResourceLoaderImport)(const String &p_path); +typedef void (*ResourceLoadedCallback)(RES p_resource, const String &p_path); + class ResourceLoader { enum { @@ -103,9 +107,12 @@ class ResourceLoader { //internal load function static RES _load(const String &p_path, const String &p_original_path, const String &p_type_hint, bool p_no_cache, Error *r_error); + static ResourceLoadedCallback _loaded_callback; + public: static Ref<ResourceInteractiveLoader> load_interactive(const String &p_path, const String &p_type_hint = "", bool p_no_cache = false, Error *r_error = NULL); static RES load(const String &p_path, const String &p_type_hint = "", bool p_no_cache = false, Error *r_error = NULL); + static bool exists(const String &p_path, const String &p_type_hint = ""); static void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions); static void add_resource_format_loader(ResourceFormatLoader *p_format_loader, bool p_at_front = false); @@ -116,6 +123,7 @@ public: static int get_import_order(const String &p_path); static void set_timestamp_on_load(bool p_timestamp) { timestamp_on_load = p_timestamp; } + static bool get_timestamp_on_load() { return timestamp_on_load; } static void notify_load_error(const String &p_err) { if (err_notify) err_notify(err_notify_ud, p_err); @@ -145,6 +153,9 @@ public: static void reload_translation_remaps(); static void load_translation_remaps(); static void clear_translation_remaps(); + + static void set_load_callback(ResourceLoadedCallback p_callback); + static ResourceLoaderImport import; }; #endif diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp index 3dcd94880a..097e81e308 100644 --- a/core/io/resource_saver.cpp +++ b/core/io/resource_saver.cpp @@ -29,10 +29,11 @@ /*************************************************************************/ #include "resource_saver.h" -#include "os/file_access.h" -#include "project_settings.h" -#include "resource_loader.h" -#include "script_language.h" + +#include "core/io/resource_loader.h" +#include "core/os/file_access.h" +#include "core/project_settings.h" +#include "core/script_language.h" ResourceFormatSaver *ResourceSaver::saver[MAX_SAVERS]; @@ -89,7 +90,7 @@ Error ResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t rwcopy->set_path(old_path); if (save_callback && p_path.begins_with("res://")) - save_callback(p_path); + save_callback(p_resource, p_path); return OK; } else { diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h index 396f37d414..cdd43292a2 100644 --- a/core/io/resource_saver.h +++ b/core/io/resource_saver.h @@ -31,7 +31,7 @@ #ifndef RESOURCE_SAVER_H #define RESOURCE_SAVER_H -#include "resource.h" +#include "core/resource.h" /** @author Juan Linietsky <reduzio@gmail.com> @@ -46,7 +46,7 @@ public: virtual ~ResourceFormatSaver() {} }; -typedef void (*ResourceSavedCallback)(const String &p_path); +typedef void (*ResourceSavedCallback)(Ref<Resource> p_resource, const String &p_path); class ResourceSaver { @@ -76,6 +76,8 @@ public: static void add_resource_format_saver(ResourceFormatSaver *p_format_saver, bool p_at_front = false); static void set_timestamp_on_save(bool p_timestamp) { timestamp_on_save = p_timestamp; } + static bool get_timestamp_on_save() { return timestamp_on_save; } + static void set_save_callback(ResourceSavedCallback p_callback); }; diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index 3e0ee088c2..3f608b720c 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "stream_peer.h" -#include "io/marshalls.h" + +#include "core/io/marshalls.h" Error StreamPeer::_put_data(const PoolVector<uint8_t> &p_data) { @@ -208,6 +209,12 @@ void StreamPeer::put_double(double p_val) { } put_data(buf, 8); } +void StreamPeer::put_string(const String &p_string) { + + CharString cs = p_string.ascii(); + put_u32(cs.length()); + put_data((const uint8_t *)cs.get_data(), cs.length()); +} void StreamPeer::put_utf8_string(const String &p_string) { CharString cs = p_string.utf8(); @@ -324,6 +331,8 @@ double StreamPeer::get_double() { } String StreamPeer::get_string(int p_bytes) { + if (p_bytes < 0) + p_bytes = get_u32(); ERR_FAIL_COND_V(p_bytes < 0, String()); Vector<char> buf; @@ -336,6 +345,8 @@ String StreamPeer::get_string(int p_bytes) { } String StreamPeer::get_utf8_string(int p_bytes) { + if (p_bytes < 0) + p_bytes = get_u32(); ERR_FAIL_COND_V(p_bytes < 0, String()); Vector<uint8_t> buf; @@ -385,6 +396,7 @@ void StreamPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("put_u64", "value"), &StreamPeer::put_u64); ClassDB::bind_method(D_METHOD("put_float", "value"), &StreamPeer::put_float); ClassDB::bind_method(D_METHOD("put_double", "value"), &StreamPeer::put_double); + ClassDB::bind_method(D_METHOD("put_string", "value"), &StreamPeer::put_string); ClassDB::bind_method(D_METHOD("put_utf8_string", "value"), &StreamPeer::put_utf8_string); ClassDB::bind_method(D_METHOD("put_var", "value"), &StreamPeer::put_var); @@ -398,8 +410,8 @@ void StreamPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_u64"), &StreamPeer::get_u64); ClassDB::bind_method(D_METHOD("get_float"), &StreamPeer::get_float); ClassDB::bind_method(D_METHOD("get_double"), &StreamPeer::get_double); - ClassDB::bind_method(D_METHOD("get_string", "bytes"), &StreamPeer::get_string); - ClassDB::bind_method(D_METHOD("get_utf8_string", "bytes"), &StreamPeer::get_utf8_string); + ClassDB::bind_method(D_METHOD("get_string", "bytes"), &StreamPeer::get_string, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("get_utf8_string", "bytes"), &StreamPeer::get_utf8_string, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("get_var"), &StreamPeer::get_var); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "big_endian"), "set_big_endian", "is_big_endian_enabled"); diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h index 605b0a7980..f189960cbd 100644 --- a/core/io/stream_peer.h +++ b/core/io/stream_peer.h @@ -31,7 +31,7 @@ #ifndef STREAM_PEER_H #define STREAM_PEER_H -#include "reference.h" +#include "core/reference.h" class StreamPeer : public Reference { GDCLASS(StreamPeer, Reference); @@ -71,6 +71,7 @@ public: void put_u64(uint64_t p_val); void put_float(float p_val); void put_double(double p_val); + void put_string(const String &p_string); void put_utf8_string(const String &p_string); void put_var(const Variant &p_variant); @@ -84,8 +85,8 @@ public: int64_t get_64(); float get_float(); double get_double(); - String get_string(int p_bytes); - String get_utf8_string(int p_bytes); + String get_string(int p_bytes = -1); + String get_utf8_string(int p_bytes = -1); Variant get_var(); StreamPeer() { big_endian = false; } diff --git a/core/io/stream_peer_ssl.cpp b/core/io/stream_peer_ssl.cpp index e7e9662d24..138f91301e 100644 --- a/core/io/stream_peer_ssl.cpp +++ b/core/io/stream_peer_ssl.cpp @@ -29,8 +29,11 @@ /*************************************************************************/ #include "stream_peer_ssl.h" -#include "os/file_access.h" -#include "project_settings.h" + +#include "core/io/certs_compressed.gen.h" +#include "core/io/compression.h" +#include "core/os/file_access.h" +#include "core/project_settings.h" StreamPeerSSL *(*StreamPeerSSL::_create)() = NULL; @@ -41,13 +44,20 @@ StreamPeerSSL *StreamPeerSSL::create() { StreamPeerSSL::LoadCertsFromMemory StreamPeerSSL::load_certs_func = NULL; bool StreamPeerSSL::available = false; -bool StreamPeerSSL::initialize_certs = true; void StreamPeerSSL::load_certs_from_memory(const PoolByteArray &p_memory) { if (load_certs_func) load_certs_func(p_memory); } +void StreamPeerSSL::load_certs_from_file(String p_path) { + if (p_path != "") { + PoolByteArray certs = get_cert_file_as_array(p_path); + if (certs.size() > 0) + load_certs_func(certs); + } +} + bool StreamPeerSSL::is_available() { return available; } @@ -60,6 +70,25 @@ bool StreamPeerSSL::is_blocking_handshake_enabled() const { return blocking_handshake; } +PoolByteArray StreamPeerSSL::get_cert_file_as_array(String p_path) { + + PoolByteArray out; + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + if (f) { + int flen = f->get_len(); + out.resize(flen + 1); + PoolByteArray::Write w = out.write(); + f->get_buffer(w.ptr(), flen); + w[flen] = 0; // Make sure it ends with string terminator + memdelete(f); +#ifdef DEBUG_ENABLED + print_verbose(vformat("Loaded certs from '%s'.", p_path)); +#endif + } + + return out; +} + PoolByteArray StreamPeerSSL::get_project_cert_array() { PoolByteArray out; @@ -67,24 +96,21 @@ PoolByteArray StreamPeerSSL::get_project_cert_array() { ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/certificates", PropertyInfo(Variant::STRING, "network/ssl/certificates", PROPERTY_HINT_FILE, "*.crt")); if (certs_path != "") { - - FileAccess *f = FileAccess::open(certs_path, FileAccess::READ); - if (f) { - int flen = f->get_len(); - out.resize(flen + 1); - { - PoolByteArray::Write w = out.write(); - f->get_buffer(w.ptr(), flen); - w[flen] = 0; //end f string - } - - memdelete(f); - + // Use certs defined in project settings. + return get_cert_file_as_array(certs_path); + } +#ifdef BUILTIN_CERTS_ENABLED + else { + // Use builtin certs only if user did not override it in project settings. + out.resize(_certs_uncompressed_size + 1); + PoolByteArray::Write w = out.write(); + Compression::decompress(w.ptr(), _certs_uncompressed_size, _certs_compressed, _certs_compressed_size, Compression::MODE_DEFLATE); + w[_certs_uncompressed_size] = 0; // Make sure it ends with string terminator #ifdef DEBUG_ENABLED - print_line("Loaded certs from '" + certs_path); + print_verbose("Loaded builtin certs"); #endif - } } +#endif return out; } @@ -102,6 +128,7 @@ void StreamPeerSSL::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "blocking_handshake"), "set_blocking_handshake_enabled", "is_blocking_handshake_enabled"); BIND_ENUM_CONSTANT(STATUS_DISCONNECTED); + BIND_ENUM_CONSTANT(STATUS_HANDSHAKING); BIND_ENUM_CONSTANT(STATUS_CONNECTED); BIND_ENUM_CONSTANT(STATUS_ERROR); BIND_ENUM_CONSTANT(STATUS_ERROR_HOSTNAME_MISMATCH); diff --git a/core/io/stream_peer_ssl.h b/core/io/stream_peer_ssl.h index 870704e875..8ce36d7e7d 100644 --- a/core/io/stream_peer_ssl.h +++ b/core/io/stream_peer_ssl.h @@ -31,7 +31,7 @@ #ifndef STREAM_PEER_SSL_H #define STREAM_PEER_SSL_H -#include "io/stream_peer.h" +#include "core/io/stream_peer.h" class StreamPeerSSL : public StreamPeer { GDCLASS(StreamPeerSSL, StreamPeer); @@ -46,9 +46,6 @@ protected: static LoadCertsFromMemory load_certs_func; static bool available; - friend class Main; - static bool initialize_certs; - bool blocking_handshake; public: @@ -72,7 +69,9 @@ public: static StreamPeerSSL *create(); + static PoolByteArray get_cert_file_as_array(String p_path); static PoolByteArray get_project_cert_array(); + static void load_certs_from_file(String p_path); static void load_certs_from_memory(const PoolByteArray &p_memory); static bool is_available(); diff --git a/core/io/stream_peer_tcp.cpp b/core/io/stream_peer_tcp.cpp index 5d008904ff..28561e8cbc 100644 --- a/core/io/stream_peer_tcp.cpp +++ b/core/io/stream_peer_tcp.cpp @@ -30,7 +30,294 @@ #include "stream_peer_tcp.h" -StreamPeerTCP *(*StreamPeerTCP::_create)() = NULL; +Error StreamPeerTCP::_poll_connection() { + + ERR_FAIL_COND_V(status != STATUS_CONNECTING || !_sock.is_valid() || !_sock->is_open(), FAILED); + + Error err = _sock->connect_to_host(peer_host, peer_port); + + if (err == OK) { + status = STATUS_CONNECTED; + return OK; + } else if (err == ERR_BUSY) { + // Still trying to connect + return OK; + } + + disconnect_from_host(); + status = STATUS_ERROR; + return ERR_CONNECTION_ERROR; +} + +void StreamPeerTCP::accept_socket(Ref<NetSocket> p_sock, IP_Address p_host, uint16_t p_port) { + + _sock = p_sock; + _sock->set_blocking_enabled(false); + + status = STATUS_CONNECTING; + + peer_host = p_host; + peer_port = p_port; +} + +Error StreamPeerTCP::connect_to_host(const IP_Address &p_host, uint16_t p_port) { + + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER); + + Error err; + IP::Type ip_type = p_host.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + + err = _sock->open(NetSocket::TYPE_TCP, ip_type); + ERR_FAIL_COND_V(err != OK, FAILED); + + _sock->set_blocking_enabled(false); + + err = _sock->connect_to_host(p_host, p_port); + + if (err == OK) { + status = STATUS_CONNECTED; + } else if (err == ERR_BUSY) { + status = STATUS_CONNECTING; + } else { + ERR_PRINT("Connection to remote host failed!"); + disconnect_from_host(); + return FAILED; + } + + peer_host = p_host; + peer_port = p_port; + + return OK; +} + +Error StreamPeerTCP::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block) { + + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + + if (status == STATUS_NONE || status == STATUS_ERROR) { + + return FAILED; + } + + if (status != STATUS_CONNECTED) { + + if (_poll_connection() != OK) { + + return FAILED; + } + + if (status != STATUS_CONNECTED) { + r_sent = 0; + return OK; + } + } + + if (!_sock->is_open()) + return FAILED; + + Error err; + int data_to_send = p_bytes; + const uint8_t *offset = p_data; + int total_sent = 0; + + while (data_to_send) { + + int sent_amount = 0; + err = _sock->send(offset, data_to_send, sent_amount); + + if (err != OK) { + + if (err != ERR_BUSY) { + disconnect_from_host(); + return FAILED; + } + + if (!p_block) { + r_sent = total_sent; + return OK; + } + + // Block and wait for the socket to accept more data + err = _sock->poll(NetSocket::POLL_TYPE_OUT, -1); + if (err != OK) { + disconnect_from_host(); + return FAILED; + } + } else { + + data_to_send -= sent_amount; + offset += sent_amount; + total_sent += sent_amount; + } + } + + r_sent = total_sent; + + return OK; +} + +Error StreamPeerTCP::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block) { + + if (!is_connected_to_host()) { + + return FAILED; + } + + if (status == STATUS_CONNECTING) { + + if (_poll_connection() != OK) { + + return FAILED; + } + + if (status != STATUS_CONNECTED) { + r_received = 0; + return OK; + } + } + + Error err; + int to_read = p_bytes; + int total_read = 0; + r_received = 0; + + while (to_read) { + + int read = 0; + err = _sock->recv(p_buffer + total_read, to_read, read); + + if (err != OK) { + + if (err != ERR_BUSY) { + disconnect_from_host(); + return FAILED; + } + + if (!p_block) { + r_received = total_read; + return OK; + } + + err = _sock->poll(NetSocket::POLL_TYPE_IN, -1); + + if (err != OK) { + disconnect_from_host(); + return FAILED; + } + + } else if (read == 0) { + + disconnect_from_host(); + r_received = total_read; + return ERR_FILE_EOF; + + } else { + + to_read -= read; + total_read += read; + } + } + + r_received = total_read; + + return OK; +} + +void StreamPeerTCP::set_no_delay(bool p_enabled) { + + ERR_FAIL_COND(!is_connected_to_host()); + _sock->set_tcp_no_delay_enabled(p_enabled); +} + +bool StreamPeerTCP::is_connected_to_host() const { + + if (status == STATUS_NONE || status == STATUS_ERROR) { + + return false; + } + + if (status != STATUS_CONNECTED) { + return true; + } + + return _sock.is_valid() && _sock->is_open(); +} + +StreamPeerTCP::Status StreamPeerTCP::get_status() { + + if (status == STATUS_CONNECTING) { + _poll_connection(); + } else if (status == STATUS_CONNECTED) { + Error err; + err = _sock->poll(NetSocket::POLL_TYPE_IN, 0); + if (err == OK) { + // FIN received + if (_sock->get_available_bytes() == 0) { + disconnect_from_host(); + return status; + } + } + // Also poll write + err = _sock->poll(NetSocket::POLL_TYPE_IN_OUT, 0); + if (err != OK && err != ERR_BUSY) { + // Got an error + disconnect_from_host(); + status = STATUS_ERROR; + } + } + + return status; +} + +void StreamPeerTCP::disconnect_from_host() { + + if (_sock.is_valid() && _sock->is_open()) + _sock->close(); + + status = STATUS_NONE; + peer_host = IP_Address(); + peer_port = 0; +} + +Error StreamPeerTCP::put_data(const uint8_t *p_data, int p_bytes) { + + int total; + return write(p_data, p_bytes, total, true); +} + +Error StreamPeerTCP::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) { + + return write(p_data, p_bytes, r_sent, false); +} + +Error StreamPeerTCP::get_data(uint8_t *p_buffer, int p_bytes) { + + int total; + return read(p_buffer, p_bytes, total, true); +} + +Error StreamPeerTCP::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) { + + return read(p_buffer, p_bytes, r_received, false); +} + +int StreamPeerTCP::get_available_bytes() const { + + ERR_FAIL_COND_V(!_sock.is_valid(), -1); + return _sock->get_available_bytes(); +} + +IP_Address StreamPeerTCP::get_connected_host() const { + + return peer_host; +} + +uint16_t StreamPeerTCP::get_connected_port() const { + + return peer_port; +} Error StreamPeerTCP::_connect(const String &p_address, int p_port) { @@ -43,8 +330,7 @@ Error StreamPeerTCP::_connect(const String &p_address, int p_port) { return ERR_CANT_RESOLVE; } - connect_to_host(ip, p_port); - return OK; + return connect_to_host(ip, p_port); } void StreamPeerTCP::_bind_methods() { @@ -63,23 +349,15 @@ void StreamPeerTCP::_bind_methods() { BIND_ENUM_CONSTANT(STATUS_ERROR); } -Ref<StreamPeerTCP> StreamPeerTCP::create_ref() { +StreamPeerTCP::StreamPeerTCP() { - if (!_create) - return Ref<StreamPeerTCP>(); - return Ref<StreamPeerTCP>(_create()); + _sock = Ref<NetSocket>(NetSocket::create()); + status = STATUS_NONE; + peer_host = IP_Address(); + peer_port = 0; } -StreamPeerTCP *StreamPeerTCP::create() { +StreamPeerTCP::~StreamPeerTCP() { - if (!_create) - return NULL; - return _create(); + disconnect_from_host(); } - -StreamPeerTCP::StreamPeerTCP() { -} - -StreamPeerTCP::~StreamPeerTCP(){ - -}; diff --git a/core/io/stream_peer_tcp.h b/core/io/stream_peer_tcp.h index 8a16d820f2..de364915cd 100644 --- a/core/io/stream_peer_tcp.h +++ b/core/io/stream_peer_tcp.h @@ -31,10 +31,10 @@ #ifndef STREAM_PEER_TCP_H #define STREAM_PEER_TCP_H -#include "stream_peer.h" - -#include "io/ip.h" -#include "ip_address.h" +#include "core/io/ip.h" +#include "core/io/ip_address.h" +#include "core/io/net_socket.h" +#include "core/io/stream_peer.h" class StreamPeerTCP : public StreamPeer { @@ -51,24 +51,37 @@ public: }; protected: - virtual Error _connect(const String &p_address, int p_port); - static StreamPeerTCP *(*_create)(); + Ref<NetSocket> _sock; + Status status; + IP_Address peer_host; + uint16_t peer_port; + + Error _connect(const String &p_address, int p_port); + Error _poll_connection(); + Error write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block); + Error read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block); + static void _bind_methods(); public: - virtual Error connect_to_host(const IP_Address &p_host, uint16_t p_port) = 0; + void accept_socket(Ref<NetSocket> p_sock, IP_Address p_host, uint16_t p_port); + + Error connect_to_host(const IP_Address &p_host, uint16_t p_port); + bool is_connected_to_host() const; + IP_Address get_connected_host() const; + uint16_t get_connected_port() const; + void disconnect_from_host(); - //read/write from streampeer + int get_available_bytes() const; + Status get_status(); - virtual bool is_connected_to_host() const = 0; - virtual Status get_status() const = 0; - virtual void disconnect_from_host() = 0; - virtual IP_Address get_connected_host() const = 0; - virtual uint16_t get_connected_port() const = 0; - virtual void set_no_delay(bool p_enabled) = 0; + void set_no_delay(bool p_enabled); - static Ref<StreamPeerTCP> create_ref(); - static StreamPeerTCP *create(); + // Read/Write from StreamPeer + Error put_data(const uint8_t *p_data, int p_bytes); + Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent); + Error get_data(uint8_t *p_buffer, int p_bytes); + Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received); StreamPeerTCP(); ~StreamPeerTCP(); diff --git a/core/io/tcp_server.cpp b/core/io/tcp_server.cpp index 5916d58390..b8194cb17f 100644 --- a/core/io/tcp_server.cpp +++ b/core/io/tcp_server.cpp @@ -30,29 +30,98 @@ #include "tcp_server.h" -TCP_Server *(*TCP_Server::_create)() = NULL; +void TCP_Server::_bind_methods() { + + ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &TCP_Server::listen, DEFVAL("*")); + ClassDB::bind_method(D_METHOD("is_connection_available"), &TCP_Server::is_connection_available); + ClassDB::bind_method(D_METHOD("take_connection"), &TCP_Server::take_connection); + ClassDB::bind_method(D_METHOD("stop"), &TCP_Server::stop); +} + +Error TCP_Server::listen(uint16_t p_port, const IP_Address &p_bind_address) { + + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER); + + Error err; + IP::Type ip_type = IP::TYPE_ANY; + + // If the bind address is valid use its type as the socket type + if (p_bind_address.is_valid()) + ip_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + + err = _sock->open(NetSocket::TYPE_TCP, ip_type); + + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + + _sock->set_blocking_enabled(false); + _sock->set_reuse_address_enabled(true); + + err = _sock->bind(p_bind_address, p_port); + + if (err != OK) { + + _sock->close(); + return ERR_ALREADY_IN_USE; + } -Ref<TCP_Server> TCP_Server::create_ref() { + err = _sock->listen(MAX_PENDING_CONNECTIONS); - if (!_create) - return NULL; - return Ref<TCP_Server>(_create()); + if (err != OK) { + _sock->close(); + return FAILED; + } + return OK; } -TCP_Server *TCP_Server::create() { +bool TCP_Server::is_connection_available() const { - if (!_create) - return NULL; - return _create(); + ERR_FAIL_COND_V(!_sock.is_valid(), false); + + if (!_sock->is_open()) + return false; + + Error err = _sock->poll(NetSocket::POLL_TYPE_IN, 0); + if (err != OK) { + return false; + } + + return true; } -void TCP_Server::_bind_methods() { +Ref<StreamPeerTCP> TCP_Server::take_connection() { - ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &TCP_Server::listen, DEFVAL("*")); - ClassDB::bind_method(D_METHOD("is_connection_available"), &TCP_Server::is_connection_available); - ClassDB::bind_method(D_METHOD("take_connection"), &TCP_Server::take_connection); - ClassDB::bind_method(D_METHOD("stop"), &TCP_Server::stop); + Ref<StreamPeerTCP> conn; + if (!is_connection_available()) { + return conn; + } + + Ref<NetSocket> ns; + IP_Address ip; + uint16_t port = 0; + ns = _sock->accept(ip, port); + if (!ns.is_valid()) + return conn; + + conn = Ref<StreamPeerTCP>(memnew(StreamPeerTCP)); + conn->accept_socket(ns, ip, port); + return conn; +} + +void TCP_Server::stop() { + + if (_sock.is_valid()) { + _sock->close(); + } } TCP_Server::TCP_Server() { + + _sock = Ref<NetSocket>(NetSocket::create()); +} + +TCP_Server::~TCP_Server() { + + stop(); } diff --git a/core/io/tcp_server.h b/core/io/tcp_server.h index a250e8b249..4c89197d9a 100644 --- a/core/io/tcp_server.h +++ b/core/io/tcp_server.h @@ -31,31 +31,32 @@ #ifndef TCP_SERVER_H #define TCP_SERVER_H -#include "io/ip.h" -#include "io/stream_peer.h" -#include "stream_peer_tcp.h" +#include "core/io/ip.h" +#include "core/io/net_socket.h" +#include "core/io/stream_peer.h" +#include "core/io/stream_peer_tcp.h" class TCP_Server : public Reference { GDCLASS(TCP_Server, Reference); protected: - static TCP_Server *(*_create)(); + enum { + MAX_PENDING_CONNECTIONS = 8 + }; - //bind helper + Ref<NetSocket> _sock; static void _bind_methods(); public: - virtual Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*")) = 0; - virtual bool is_connection_available() const = 0; - virtual Ref<StreamPeerTCP> take_connection() = 0; + Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*")); + bool is_connection_available() const; + Ref<StreamPeerTCP> take_connection(); - virtual void stop() = 0; //stop listening - - static Ref<TCP_Server> create_ref(); - static TCP_Server *create(); + void stop(); // Stop listening TCP_Server(); + ~TCP_Server(); }; #endif // TCP_SERVER_H diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp index 85c1fc5ddf..830c2b6694 100644 --- a/core/io/translation_loader_po.cpp +++ b/core/io/translation_loader_po.cpp @@ -29,8 +29,9 @@ /*************************************************************************/ #include "translation_loader_po.h" -#include "os/file_access.h" -#include "translation.h" + +#include "core/os/file_access.h" +#include "core/translation.h" RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const String &p_path) { diff --git a/core/io/translation_loader_po.h b/core/io/translation_loader_po.h index 33cf9bd8b4..670a9fdd7e 100644 --- a/core/io/translation_loader_po.h +++ b/core/io/translation_loader_po.h @@ -31,9 +31,10 @@ #ifndef TRANSLATION_LOADER_PO_H #define TRANSLATION_LOADER_PO_H -#include "io/resource_loader.h" -#include "os/file_access.h" -#include "translation.h" +#include "core/io/resource_loader.h" +#include "core/os/file_access.h" +#include "core/translation.h" + class TranslationLoaderPO : public ResourceFormatLoader { public: static RES load_translation(FileAccess *f, Error *r_error, const String &p_path = String()); diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp index 33c9b56d5a..8c0cbd6e55 100644 --- a/core/io/xml_parser.cpp +++ b/core/io/xml_parser.cpp @@ -29,7 +29,9 @@ /*************************************************************************/ #include "xml_parser.h" -#include "print_string.h" + +#include "core/print_string.h" + //#define DEBUG_XML VARIANT_ENUM_CAST(XMLParser::NodeType); diff --git a/core/io/xml_parser.h b/core/io/xml_parser.h index 297b57ffdc..bbc7671a0e 100644 --- a/core/io/xml_parser.h +++ b/core/io/xml_parser.h @@ -31,10 +31,10 @@ #ifndef XML_PARSER_H #define XML_PARSER_H -#include "os/file_access.h" -#include "reference.h" -#include "ustring.h" -#include "vector.h" +#include "core/os/file_access.h" +#include "core/reference.h" +#include "core/ustring.h" +#include "core/vector.h" /* Based on irrXML (see their zlib license). Added mainly for compatibility with their Collada loader. diff --git a/core/io/zip_io.cpp b/core/io/zip_io.cpp new file mode 100644 index 0000000000..b7f841b66f --- /dev/null +++ b/core/io/zip_io.cpp @@ -0,0 +1,137 @@ +/*************************************************************************/ +/* zip_io.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "zip_io.h" + +#include "core/os/copymem.h" + +void *zipio_open(void *data, const char *p_fname, int mode) { + + FileAccess *&f = *(FileAccess **)data; + + String fname; + fname.parse_utf8(p_fname); + + if (mode & ZLIB_FILEFUNC_MODE_WRITE) { + f = FileAccess::open(fname, FileAccess::WRITE); + } else { + + f = FileAccess::open(fname, FileAccess::READ); + } + + if (!f) + return NULL; + + return data; +} + +uLong zipio_read(void *data, void *fdata, void *buf, uLong size) { + + FileAccess *f = *(FileAccess **)data; + return f->get_buffer((uint8_t *)buf, size); +} + +uLong zipio_write(voidpf opaque, voidpf stream, const void *buf, uLong size) { + + FileAccess *f = *(FileAccess **)opaque; + f->store_buffer((uint8_t *)buf, size); + return size; +} + +long zipio_tell(voidpf opaque, voidpf stream) { + + FileAccess *f = *(FileAccess **)opaque; + return f->get_position(); +} + +long zipio_seek(voidpf opaque, voidpf stream, uLong offset, int origin) { + + FileAccess *f = *(FileAccess **)opaque; + + int pos = offset; + switch (origin) { + + case ZLIB_FILEFUNC_SEEK_CUR: + pos = f->get_position() + offset; + break; + case ZLIB_FILEFUNC_SEEK_END: + pos = f->get_len() + offset; + break; + default: + break; + }; + + f->seek(pos); + return 0; +} + +int zipio_close(voidpf opaque, voidpf stream) { + + FileAccess *&f = *(FileAccess **)opaque; + if (f) { + f->close(); + f = NULL; + } + return 0; +} + +int zipio_testerror(voidpf opaque, voidpf stream) { + + FileAccess *f = *(FileAccess **)opaque; + return (f && f->get_error() != OK) ? 1 : 0; +} + +voidpf zipio_alloc(voidpf opaque, uInt items, uInt size) { + + voidpf ptr = memalloc(items * size); + zeromem(ptr, items * size); + return ptr; +} + +void zipio_free(voidpf opaque, voidpf address) { + + memfree(address); +} + +zlib_filefunc_def zipio_create_io_from_file(FileAccess **p_file) { + + zlib_filefunc_def io; + io.opaque = p_file; + io.zopen_file = zipio_open; + io.zread_file = zipio_read; + io.zwrite_file = zipio_write; + io.ztell_file = zipio_tell; + io.zseek_file = zipio_seek; + io.zclose_file = zipio_close; + io.zerror_file = zipio_testerror; + io.alloc_mem = zipio_alloc; + io.free_mem = zipio_free; + return io; +} diff --git a/core/io/zip_io.h b/core/io/zip_io.h index 3a7fdb0302..bba7d67332 100644 --- a/core/io/zip_io.h +++ b/core/io/zip_io.h @@ -31,114 +31,28 @@ #ifndef ZIP_IO_H #define ZIP_IO_H -#include "os/copymem.h" -#include "os/file_access.h" +#include "core/os/file_access.h" +// Not direclty used in this header, but assumed available in downstream users +// like platform/*/export/export.cpp. Could be fixed, but probably better to have +// thirdparty includes in as little headers as possible. #include "thirdparty/minizip/unzip.h" #include "thirdparty/minizip/zip.h" -static void *zipio_open(void *data, const char *p_fname, int mode) { +void *zipio_open(void *data, const char *p_fname, int mode); +uLong zipio_read(void *data, void *fdata, void *buf, uLong size); +uLong zipio_write(voidpf opaque, voidpf stream, const void *buf, uLong size); - FileAccess *&f = *(FileAccess **)data; +long zipio_tell(voidpf opaque, voidpf stream); +long zipio_seek(voidpf opaque, voidpf stream, uLong offset, int origin); - String fname; - fname.parse_utf8(p_fname); +int zipio_close(voidpf opaque, voidpf stream); - if (mode & ZLIB_FILEFUNC_MODE_WRITE) { - f = FileAccess::open(fname, FileAccess::WRITE); - } else { +int zipio_testerror(voidpf opaque, voidpf stream); - f = FileAccess::open(fname, FileAccess::READ); - } +voidpf zipio_alloc(voidpf opaque, uInt items, uInt size); +void zipio_free(voidpf opaque, voidpf address); - if (!f) - return NULL; - - return data; -}; - -static uLong zipio_read(void *data, void *fdata, void *buf, uLong size) { - - FileAccess *f = *(FileAccess **)data; - return f->get_buffer((uint8_t *)buf, size); -}; - -static uLong zipio_write(voidpf opaque, voidpf stream, const void *buf, uLong size) { - - FileAccess *f = *(FileAccess **)opaque; - f->store_buffer((uint8_t *)buf, size); - return size; -}; - -static long zipio_tell(voidpf opaque, voidpf stream) { - - FileAccess *f = *(FileAccess **)opaque; - return f->get_position(); -}; - -static long zipio_seek(voidpf opaque, voidpf stream, uLong offset, int origin) { - - FileAccess *f = *(FileAccess **)opaque; - - int pos = offset; - switch (origin) { - - case ZLIB_FILEFUNC_SEEK_CUR: - pos = f->get_position() + offset; - break; - case ZLIB_FILEFUNC_SEEK_END: - pos = f->get_len() + offset; - break; - default: - break; - }; - - f->seek(pos); - return 0; -}; - -static int zipio_close(voidpf opaque, voidpf stream) { - - FileAccess *&f = *(FileAccess **)opaque; - if (f) { - f->close(); - f = NULL; - } - return 0; -}; - -static int zipio_testerror(voidpf opaque, voidpf stream) { - - FileAccess *f = *(FileAccess **)opaque; - return (f && f->get_error() != OK) ? 1 : 0; -}; - -static voidpf zipio_alloc(voidpf opaque, uInt items, uInt size) { - - voidpf ptr = memalloc(items * size); - zeromem(ptr, items * size); - return ptr; -} - -static void zipio_free(voidpf opaque, voidpf address) { - - memfree(address); -} - -static zlib_filefunc_def zipio_create_io_from_file(FileAccess **p_file) { - - zlib_filefunc_def io; - io.opaque = p_file; - io.zopen_file = zipio_open; - io.zread_file = zipio_read; - io.zwrite_file = zipio_write; - io.ztell_file = zipio_tell; - io.zseek_file = zipio_seek; - io.zclose_file = zipio_close; - io.zerror_file = zipio_testerror; - io.alloc_mem = zipio_alloc; - io.free_mem = zipio_free; - return io; -} +zlib_filefunc_def zipio_create_io_from_file(FileAccess **p_file); #endif // ZIP_IO_H diff --git a/core/list.h b/core/list.h index f977df4634..281f253a59 100644 --- a/core/list.h +++ b/core/list.h @@ -31,8 +31,8 @@ #ifndef GLOBALS_LIST_H #define GLOBALS_LIST_H -#include "os/memory.h" -#include "sort.h" +#include "core/os/memory.h" +#include "core/sort.h" /** * Generic Templatized Linked List Implementation. diff --git a/core/map.h b/core/map.h index 700d4b8693..cab8965bb3 100644 --- a/core/map.h +++ b/core/map.h @@ -31,7 +31,7 @@ #ifndef MAP_H #define MAP_H -#include "set.h" +#include "core/set.h" /** @author Juan Linietsky <reduzio@gmail.com> diff --git a/core/math/SCsub b/core/math/SCsub index 4efc902717..1c5f954470 100644 --- a/core/math/SCsub +++ b/core/math/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.core_sources, "*.cpp") - -Export('env') diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index 021391da83..451c45cade 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -29,9 +29,10 @@ /*************************************************************************/ #include "a_star.h" -#include "geometry.h" + +#include "core/math/geometry.h" +#include "core/script_language.h" #include "scene/scene_string_names.h" -#include "script_language.h" int AStar::get_available_point_id() const { @@ -249,14 +250,9 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { n->distance = _compute_cost(begin_point->id, n->id) * n->weight_scale; n->last_pass = pass; open_list.add(&n->list); - - if (end_point == n) { - found_route = true; - break; - } } - while (!found_route) { + while (true) { if (open_list.first() == NULL) { // No path found @@ -276,13 +272,16 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { cost += _estimate_cost(p->id, end_point->id); if (cost < least_cost) { - least_cost_point = E; least_cost = cost; } } Point *p = least_cost_point->self(); + if (p == end_point) { + found_route = true; + break; + } for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) { @@ -294,7 +293,6 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { // Already visited, is this cheaper? if (e->distance > distance) { - e->prev_point = p; e->distance = distance; } @@ -305,18 +303,9 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { e->distance = distance; e->last_pass = pass; // Mark as used open_list.add(&e->list); - - if (e == end_point) { - // End reached; stop algorithm - found_route = true; - break; - } } } - if (found_route) - break; - open_list.remove(least_cost_point); } diff --git a/core/math/a_star.h b/core/math/a_star.h index 8c1b5f64cb..d2ef765006 100644 --- a/core/math/a_star.h +++ b/core/math/a_star.h @@ -31,8 +31,9 @@ #ifndef ASTAR_H #define ASTAR_H -#include "reference.h" -#include "self_list.h" +#include "core/reference.h" +#include "core/self_list.h" + /** A* pathfinding algorithm diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp index e2e71dda92..d0cb2b5195 100644 --- a/core/math/aabb.cpp +++ b/core/math/aabb.cpp @@ -30,7 +30,7 @@ #include "aabb.h" -#include "print_string.h" +#include "core/print_string.h" real_t AABB::get_area() const { diff --git a/core/math/aabb.h b/core/math/aabb.h index cdb8eb48a3..0b03b7d314 100644 --- a/core/math/aabb.h +++ b/core/math/aabb.h @@ -31,9 +31,9 @@ #ifndef AABB_H #define AABB_H -#include "math_defs.h" -#include "plane.h" -#include "vector3.h" +#include "core/math/math_defs.h" +#include "core/math/plane.h" +#include "core/math/vector3.h" /** * AABB / AABB (Axis Aligned Bounding Box) diff --git a/core/math/audio_frame.h b/core/math/audio_frame.h index 67ba025e1c..fde26e8056 100644 --- a/core/math/audio_frame.h +++ b/core/math/audio_frame.h @@ -31,7 +31,7 @@ #ifndef AUDIOFRAME_H #define AUDIOFRAME_H -#include "typedefs.h" +#include "core/typedefs.h" static inline float undenormalise(volatile float f) { union { diff --git a/core/math/bsp_tree.cpp b/core/math/bsp_tree.cpp index 24096de551..6ffc963783 100644 --- a/core/math/bsp_tree.cpp +++ b/core/math/bsp_tree.cpp @@ -29,8 +29,9 @@ /*************************************************************************/ #include "bsp_tree.h" -#include "error_macros.h" -#include "print_string.h" + +#include "core/error_macros.h" +#include "core/print_string.h" void BSP_Tree::from_aabb(const AABB &p_aabb) { @@ -164,7 +165,6 @@ int BSP_Tree::get_points_inside(const Vector3 *p_points, int p_point_count) cons int pass_count = 0; const Node *nodesptr = &nodes[0]; const Plane *planesptr = &planes[0]; - int plane_count = planes.size(); int node_count = nodes.size(); if (node_count == 0) // no nodes! @@ -191,9 +191,9 @@ int BSP_Tree::get_points_inside(const Vector3 *p_points, int p_point_count) cons break; } - uint16_t plane = nodesptr[idx].plane; #ifdef DEBUG_ENABLED - + int plane_count = planes.size(); + uint16_t plane = nodesptr[idx].plane; ERR_FAIL_INDEX_V(plane, plane_count, false); #endif diff --git a/core/math/bsp_tree.h b/core/math/bsp_tree.h index fb16818ae7..b06e6b8539 100644 --- a/core/math/bsp_tree.h +++ b/core/math/bsp_tree.h @@ -31,13 +31,13 @@ #ifndef BSP_TREE_H #define BSP_TREE_H -#include "aabb.h" -#include "dvector.h" -#include "face3.h" -#include "method_ptrcall.h" -#include "plane.h" -#include "variant.h" -#include "vector.h" +#include "core/dvector.h" +#include "core/math/aabb.h" +#include "core/math/face3.h" +#include "core/math/plane.h" +#include "core/method_ptrcall.h" +#include "core/variant.h" +#include "core/vector.h" /** @author Juan Linietsky <reduzio@gmail.com> */ diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp index 1ab9b3532e..3a082d5720 100644 --- a/core/math/camera_matrix.cpp +++ b/core/math/camera_matrix.cpp @@ -29,8 +29,9 @@ /*************************************************************************/ #include "camera_matrix.h" -#include "math_funcs.h" -#include "print_string.h" + +#include "core/math/math_funcs.h" +#include "core/print_string.h" void CameraMatrix::set_identity() { diff --git a/core/math/camera_matrix.h b/core/math/camera_matrix.h index 226b4d572b..bd20908ad9 100644 --- a/core/math/camera_matrix.h +++ b/core/math/camera_matrix.h @@ -31,8 +31,9 @@ #ifndef CAMERA_MATRIX_H #define CAMERA_MATRIX_H -#include "math_2d.h" -#include "transform.h" +#include "core/math/rect2.h" +#include "core/math/transform.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ diff --git a/core/math/delaunay.cpp b/core/math/delaunay.cpp deleted file mode 100644 index 8cae92b7c0..0000000000 --- a/core/math/delaunay.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "delaunay.h" diff --git a/core/math/delaunay.h b/core/math/delaunay.h index 13fbc0c6ae..9c5eef9069 100644 --- a/core/math/delaunay.h +++ b/core/math/delaunay.h @@ -1,7 +1,37 @@ +/*************************************************************************/ +/* delaunay.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + #ifndef DELAUNAY_H #define DELAUNAY_H -#include "math_2d.h" +#include "core/math/rect2.h" class Delaunay2D { public: diff --git a/core/math/expression.cpp b/core/math/expression.cpp new file mode 100644 index 0000000000..0cfb54234c --- /dev/null +++ b/core/math/expression.cpp @@ -0,0 +1,2174 @@ +/*************************************************************************/ +/* expression.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "expression.h" + +#include "core/class_db.h" +#include "core/func_ref.h" +#include "core/io/marshalls.h" +#include "core/math/math_funcs.h" +#include "core/os/os.h" +#include "core/reference.h" +#include "core/variant_parser.h" + +const char *Expression::func_name[Expression::FUNC_MAX] = { + "sin", + "cos", + "tan", + "sinh", + "cosh", + "tanh", + "asin", + "acos", + "atan", + "atan2", + "sqrt", + "fmod", + "fposmod", + "floor", + "ceil", + "round", + "abs", + "sign", + "pow", + "log", + "exp", + "is_nan", + "is_inf", + "ease", + "decimals", + "stepify", + "lerp", + "inverse_lerp", + "range_lerp", + "dectime", + "randomize", + "randi", + "randf", + "rand_range", + "seed", + "rand_seed", + "deg2rad", + "rad2deg", + "linear2db", + "db2linear", + "polar2cartesian", + "cartesian2polar", + "wrapi", + "wrapf", + "max", + "min", + "clamp", + "nearest_po2", + "weakref", + "funcref", + "convert", + "typeof", + "type_exists", + "char", + "str", + "print", + "printerr", + "printraw", + "var2str", + "str2var", + "var2bytes", + "bytes2var", + "color_named", +}; + +Expression::BuiltinFunc Expression::find_function(const String &p_string) { + + for (int i = 0; i < FUNC_MAX; i++) { + if (p_string == func_name[i]) + return BuiltinFunc(i); + } + + return FUNC_MAX; +} + +String Expression::get_func_name(BuiltinFunc p_func) { + + ERR_FAIL_INDEX_V(p_func, FUNC_MAX, String()); + return func_name[p_func]; +} + +int Expression::get_func_argument_count(BuiltinFunc p_func) { + + switch (p_func) { + + case MATH_RANDOMIZE: + case MATH_RAND: + case MATH_RANDF: + return 0; + case MATH_SIN: + case MATH_COS: + case MATH_TAN: + case MATH_SINH: + case MATH_COSH: + case MATH_TANH: + case MATH_ASIN: + case MATH_ACOS: + case MATH_ATAN: + case MATH_SQRT: + case MATH_FLOOR: + case MATH_CEIL: + case MATH_ROUND: + case MATH_ABS: + case MATH_SIGN: + case MATH_LOG: + case MATH_EXP: + case MATH_ISNAN: + case MATH_ISINF: + case MATH_DECIMALS: + case MATH_SEED: + case MATH_RANDSEED: + case MATH_DEG2RAD: + case MATH_RAD2DEG: + case MATH_LINEAR2DB: + case MATH_DB2LINEAR: + case LOGIC_NEAREST_PO2: + case OBJ_WEAKREF: + case TYPE_OF: + case TEXT_CHAR: + case TEXT_STR: + case TEXT_PRINT: + case TEXT_PRINTERR: + case TEXT_PRINTRAW: + case VAR_TO_STR: + case STR_TO_VAR: + case VAR_TO_BYTES: + case BYTES_TO_VAR: + case TYPE_EXISTS: + return 1; + case MATH_ATAN2: + case MATH_FMOD: + case MATH_FPOSMOD: + case MATH_POW: + case MATH_EASE: + case MATH_STEPIFY: + case MATH_RANDOM: + case MATH_POLAR2CARTESIAN: + case MATH_CARTESIAN2POLAR: + case LOGIC_MAX: + case LOGIC_MIN: + case FUNC_FUNCREF: + case TYPE_CONVERT: + case COLORN: + return 2; + case MATH_LERP: + case MATH_INVERSE_LERP: + case MATH_DECTIME: + case MATH_WRAP: + case MATH_WRAPF: + case LOGIC_CLAMP: + return 3; + case MATH_RANGE_LERP: + return 5; + case FUNC_MAX: { + } + } + return 0; +} + +#define VALIDATE_ARG_NUM(m_arg) \ + if (!p_inputs[m_arg]->is_num()) { \ + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; \ + r_error.argument = m_arg; \ + r_error.expected = Variant::REAL; \ + return; \ + } + +void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant *r_return, Variant::CallError &r_error, String &r_error_str) { + r_error.error = Variant::CallError::CALL_OK; + switch (p_func) { + case MATH_SIN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::sin((double)*p_inputs[0]); + } break; + case MATH_COS: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::cos((double)*p_inputs[0]); + } break; + case MATH_TAN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::tan((double)*p_inputs[0]); + } break; + case MATH_SINH: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::sinh((double)*p_inputs[0]); + } break; + case MATH_COSH: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::cosh((double)*p_inputs[0]); + } break; + case MATH_TANH: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::tanh((double)*p_inputs[0]); + } break; + case MATH_ASIN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::asin((double)*p_inputs[0]); + } break; + case MATH_ACOS: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::acos((double)*p_inputs[0]); + } break; + case MATH_ATAN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::atan((double)*p_inputs[0]); + } break; + case MATH_ATAN2: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::atan2((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_SQRT: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::sqrt((double)*p_inputs[0]); + } break; + case MATH_FMOD: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::fmod((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_FPOSMOD: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::fposmod((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_FLOOR: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::floor((double)*p_inputs[0]); + } break; + case MATH_CEIL: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::ceil((double)*p_inputs[0]); + } break; + case MATH_ROUND: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::round((double)*p_inputs[0]); + } break; + case MATH_ABS: { + + if (p_inputs[0]->get_type() == Variant::INT) { + + int64_t i = *p_inputs[0]; + *r_return = ABS(i); + } else if (p_inputs[0]->get_type() == Variant::REAL) { + + real_t r = *p_inputs[0]; + *r_return = Math::abs(r); + } else { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::REAL; + } + } break; + case MATH_SIGN: { + + if (p_inputs[0]->get_type() == Variant::INT) { + + int64_t i = *p_inputs[0]; + *r_return = i < 0 ? -1 : (i > 0 ? +1 : 0); + } else if (p_inputs[0]->get_type() == Variant::REAL) { + + real_t r = *p_inputs[0]; + *r_return = r < 0.0 ? -1.0 : (r > 0.0 ? +1.0 : 0.0); + } else { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::REAL; + } + } break; + case MATH_POW: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::pow((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_LOG: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::log((double)*p_inputs[0]); + } break; + case MATH_EXP: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::exp((double)*p_inputs[0]); + } break; + case MATH_ISNAN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::is_nan((double)*p_inputs[0]); + } break; + case MATH_ISINF: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::is_inf((double)*p_inputs[0]); + } break; + case MATH_EASE: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::ease((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_DECIMALS: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::step_decimals((double)*p_inputs[0]); + } break; + case MATH_STEPIFY: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::stepify((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_LERP: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case MATH_INVERSE_LERP: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::inverse_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case MATH_RANGE_LERP: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + VALIDATE_ARG_NUM(3); + VALIDATE_ARG_NUM(4); + *r_return = Math::range_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2], (double)*p_inputs[3], (double)*p_inputs[4]); + } break; + case MATH_DECTIME: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::dectime((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case MATH_RANDOMIZE: { + Math::randomize(); + + } break; + case MATH_RAND: { + *r_return = Math::rand(); + } break; + case MATH_RANDF: { + *r_return = Math::randf(); + } break; + case MATH_RANDOM: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::random((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_SEED: { + + VALIDATE_ARG_NUM(0); + uint64_t seed = *p_inputs[0]; + Math::seed(seed); + + } break; + case MATH_RANDSEED: { + + VALIDATE_ARG_NUM(0); + uint64_t seed = *p_inputs[0]; + int ret = Math::rand_from_seed(&seed); + Array reta; + reta.push_back(ret); + reta.push_back(seed); + *r_return = reta; + + } break; + case MATH_DEG2RAD: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::deg2rad((double)*p_inputs[0]); + } break; + case MATH_RAD2DEG: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::rad2deg((double)*p_inputs[0]); + } break; + case MATH_LINEAR2DB: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::linear2db((double)*p_inputs[0]); + } break; + case MATH_DB2LINEAR: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::db2linear((double)*p_inputs[0]); + } break; + case MATH_POLAR2CARTESIAN: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + double r = *p_inputs[0]; + double th = *p_inputs[1]; + *r_return = Vector2(r * Math::cos(th), r * Math::sin(th)); + } break; + case MATH_CARTESIAN2POLAR: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + double x = *p_inputs[0]; + double y = *p_inputs[1]; + *r_return = Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x)); + } break; + case MATH_WRAP: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::wrapi((int64_t)*p_inputs[0], (int64_t)*p_inputs[1], (int64_t)*p_inputs[2]); + } break; + case MATH_WRAPF: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::wrapf((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case LOGIC_MAX: { + + if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT) { + + int64_t a = *p_inputs[0]; + int64_t b = *p_inputs[1]; + *r_return = MAX(a, b); + } else { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + + real_t a = *p_inputs[0]; + real_t b = *p_inputs[1]; + + *r_return = MAX(a, b); + } + + } break; + case LOGIC_MIN: { + + if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT) { + + int64_t a = *p_inputs[0]; + int64_t b = *p_inputs[1]; + *r_return = MIN(a, b); + } else { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + + real_t a = *p_inputs[0]; + real_t b = *p_inputs[1]; + + *r_return = MIN(a, b); + } + } break; + case LOGIC_CLAMP: { + + if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT && p_inputs[2]->get_type() == Variant::INT) { + + int64_t a = *p_inputs[0]; + int64_t b = *p_inputs[1]; + int64_t c = *p_inputs[2]; + *r_return = CLAMP(a, b, c); + } else { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + + real_t a = *p_inputs[0]; + real_t b = *p_inputs[1]; + real_t c = *p_inputs[2]; + + *r_return = CLAMP(a, b, c); + } + } break; + case LOGIC_NEAREST_PO2: { + + VALIDATE_ARG_NUM(0); + int64_t num = *p_inputs[0]; + *r_return = next_power_of_2(num); + } break; + case OBJ_WEAKREF: { + + if (p_inputs[0]->get_type() != Variant::OBJECT) { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + + return; + } + + if (p_inputs[0]->is_ref()) { + + REF r = *p_inputs[0]; + if (!r.is_valid()) { + + return; + } + + Ref<WeakRef> wref = memnew(WeakRef); + wref->set_ref(r); + *r_return = wref; + } else { + Object *obj = *p_inputs[0]; + if (!obj) { + + return; + } + Ref<WeakRef> wref = memnew(WeakRef); + wref->set_obj(obj); + *r_return = wref; + } + + } break; + case FUNC_FUNCREF: { + + if (p_inputs[0]->get_type() != Variant::OBJECT) { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + + return; + } + if (p_inputs[1]->get_type() != Variant::STRING && p_inputs[1]->get_type() != Variant::NODE_PATH) { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = Variant::STRING; + + return; + } + + Ref<FuncRef> fr = memnew(FuncRef); + + fr->set_instance(*p_inputs[0]); + fr->set_function(*p_inputs[1]); + + *r_return = fr; + + } break; + case TYPE_CONVERT: { + + VALIDATE_ARG_NUM(1); + int type = *p_inputs[1]; + if (type < 0 || type >= Variant::VARIANT_MAX) { + + r_error_str = RTR("Invalid type argument to convert(), use TYPE_* constants."); + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::INT; + return; + + } else { + + *r_return = Variant::construct(Variant::Type(type), p_inputs, 1, r_error); + } + } break; + case TYPE_OF: { + + *r_return = p_inputs[0]->get_type(); + + } break; + case TYPE_EXISTS: { + + *r_return = ClassDB::class_exists(*p_inputs[0]); + + } break; + case TEXT_CHAR: { + + CharType result[2] = { *p_inputs[0], 0 }; + + *r_return = String(result); + + } break; + case TEXT_STR: { + + String str = *p_inputs[0]; + + *r_return = str; + + } break; + case TEXT_PRINT: { + + String str = *p_inputs[0]; + print_line(str); + + } break; + + case TEXT_PRINTERR: { + + String str = *p_inputs[0]; + print_error(str); + + } break; + case TEXT_PRINTRAW: { + + String str = *p_inputs[0]; + OS::get_singleton()->print("%s", str.utf8().get_data()); + + } break; + case VAR_TO_STR: { + + String vars; + VariantWriter::write_to_string(*p_inputs[0], vars); + *r_return = vars; + } break; + case STR_TO_VAR: { + + if (p_inputs[0]->get_type() != Variant::STRING) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::STRING; + + return; + } + + VariantParser::StreamString ss; + ss.s = *p_inputs[0]; + + String errs; + int line; + Error err = VariantParser::parse(&ss, *r_return, errs, line); + + if (err != OK) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::STRING; + *r_return = "Parse error at line " + itos(line) + ": " + errs; + return; + } + + } break; + case VAR_TO_BYTES: { + + PoolByteArray barr; + int len; + Error err = encode_variant(*p_inputs[0], NULL, len); + if (err) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::NIL; + r_error_str = "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."; + return; + } + + barr.resize(len); + { + PoolByteArray::Write w = barr.write(); + encode_variant(*p_inputs[0], w.ptr(), len); + } + *r_return = barr; + } break; + case BYTES_TO_VAR: { + + if (p_inputs[0]->get_type() != Variant::POOL_BYTE_ARRAY) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::POOL_BYTE_ARRAY; + + return; + } + + PoolByteArray varr = *p_inputs[0]; + Variant ret; + { + PoolByteArray::Read r = varr.read(); + Error err = decode_variant(ret, r.ptr(), varr.size(), NULL); + if (err != OK) { + r_error_str = RTR("Not enough bytes for decoding bytes, or invalid format."); + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::POOL_BYTE_ARRAY; + return; + } + } + + *r_return = ret; + + } break; + case COLORN: { + + VALIDATE_ARG_NUM(1); + + Color color = Color::named(*p_inputs[0]); + color.a = *p_inputs[1]; + + *r_return = String(color); + + } break; + default: {} + } +} + +//////// + +static bool _is_number(CharType c) { + return (c >= '0' && c <= '9'); +} + +Error Expression::_get_token(Token &r_token) { + + while (true) { +#define GET_CHAR() (str_ofs >= expression.length() ? 0 : expression[str_ofs++]) + + CharType cchar = GET_CHAR(); + if (cchar == 0) { + r_token.type = TK_EOF; + return OK; + } + + switch (cchar) { + + case 0: { + r_token.type = TK_EOF; + return OK; + } break; + case '{': { + + r_token.type = TK_CURLY_BRACKET_OPEN; + return OK; + }; + case '}': { + + r_token.type = TK_CURLY_BRACKET_CLOSE; + return OK; + }; + case '[': { + + r_token.type = TK_BRACKET_OPEN; + return OK; + }; + case ']': { + + r_token.type = TK_BRACKET_CLOSE; + return OK; + }; + case '(': { + + r_token.type = TK_PARENTHESIS_OPEN; + return OK; + }; + case ')': { + + r_token.type = TK_PARENTHESIS_CLOSE; + return OK; + }; + case ',': { + + r_token.type = TK_COMMA; + return OK; + }; + case ':': { + + r_token.type = TK_COLON; + return OK; + }; + case '$': { + + r_token.type = TK_INPUT; + int index = 0; + do { + if (!_is_number(expression[str_ofs])) { + _set_error("Expected number after '$'"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + index *= 10; + index += expression[str_ofs] - '0'; + str_ofs++; + + } while (_is_number(expression[str_ofs])); + + r_token.value = index; + return OK; + }; + case '=': { + + cchar = GET_CHAR(); + if (cchar == '=') { + r_token.type = TK_OP_EQUAL; + } else { + _set_error("Expected '='"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + return OK; + }; + case '!': { + + if (expression[str_ofs] == '=') { + r_token.type = TK_OP_NOT_EQUAL; + str_ofs++; + } else { + r_token.type = TK_OP_NOT; + } + return OK; + }; + case '>': { + + if (expression[str_ofs] == '=') { + r_token.type = TK_OP_GREATER_EQUAL; + str_ofs++; + } else if (expression[str_ofs] == '>') { + r_token.type = TK_OP_SHIFT_RIGHT; + str_ofs++; + } else { + r_token.type = TK_OP_GREATER; + } + return OK; + }; + case '<': { + + if (expression[str_ofs] == '=') { + r_token.type = TK_OP_LESS_EQUAL; + str_ofs++; + } else if (expression[str_ofs] == '<') { + r_token.type = TK_OP_SHIFT_LEFT; + str_ofs++; + } else { + r_token.type = TK_OP_LESS; + } + return OK; + }; + case '+': { + r_token.type = TK_OP_ADD; + return OK; + }; + case '-': { + r_token.type = TK_OP_SUB; + return OK; + }; + case '/': { + r_token.type = TK_OP_DIV; + return OK; + }; + case '*': { + r_token.type = TK_OP_MUL; + return OK; + }; + case '%': { + r_token.type = TK_OP_MOD; + return OK; + }; + case '&': { + + if (expression[str_ofs] == '&') { + r_token.type = TK_OP_AND; + str_ofs++; + } else { + r_token.type = TK_OP_BIT_AND; + } + return OK; + }; + case '|': { + + if (expression[str_ofs] == '|') { + r_token.type = TK_OP_OR; + str_ofs++; + } else { + r_token.type = TK_OP_BIT_OR; + } + return OK; + }; + case '^': { + + r_token.type = TK_OP_BIT_XOR; + + return OK; + }; + case '~': { + + r_token.type = TK_OP_BIT_INVERT; + + return OK; + }; + case '"': { + + String str; + while (true) { + + CharType ch = GET_CHAR(); + + if (ch == 0) { + _set_error("Unterminated String"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } else if (ch == '"') { + break; + } else if (ch == '\\') { + //escaped characters... + + CharType next = GET_CHAR(); + if (next == 0) { + _set_error("Unterminated String"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + CharType res = 0; + + switch (next) { + + case 'b': res = 8; break; + case 't': res = 9; break; + case 'n': res = 10; break; + case 'f': res = 12; break; + case 'r': res = 13; break; + case 'u': { + //hexnumbarh - oct is deprecated + + for (int j = 0; j < 4; j++) { + CharType c = GET_CHAR(); + + if (c == 0) { + _set_error("Unterminated String"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + if (!(_is_number(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { + + _set_error("Malformed hex constant in string"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + CharType v; + if (_is_number(c)) { + v = c - '0'; + } else if (c >= 'a' && c <= 'f') { + v = c - 'a'; + v += 10; + } else if (c >= 'A' && c <= 'F') { + v = c - 'A'; + v += 10; + } else { + ERR_PRINT("BUG"); + v = 0; + } + + res <<= 4; + res |= v; + } + + } break; + //case '\"': res='\"'; break; + //case '\\': res='\\'; break; + //case '/': res='/'; break; + default: { + res = next; + //r_err_str="Invalid escape sequence"; + //return ERR_PARSE_ERROR; + } break; + } + + str += res; + + } else { + str += ch; + } + } + + r_token.type = TK_CONSTANT; + r_token.value = str; + return OK; + + } break; + default: { + + if (cchar <= 32) { + break; + } + + CharType next_char = (str_ofs >= expression.length()) ? 0 : expression[str_ofs]; + if (_is_number(cchar) || (cchar == '.' && _is_number(next_char))) { + //a number + + String num; +#define READING_SIGN 0 +#define READING_INT 1 +#define READING_DEC 2 +#define READING_EXP 3 +#define READING_DONE 4 + int reading = READING_INT; + + CharType c = cchar; + bool exp_sign = false; + bool exp_beg = false; + bool is_float = false; + + while (true) { + + switch (reading) { + case READING_INT: { + + if (_is_number(c)) { + //pass + } else if (c == '.') { + reading = READING_DEC; + is_float = true; + } else if (c == 'e') { + reading = READING_EXP; + } else { + reading = READING_DONE; + } + + } break; + case READING_DEC: { + + if (_is_number(c)) { + + } else if (c == 'e') { + reading = READING_EXP; + + } else { + reading = READING_DONE; + } + + } break; + case READING_EXP: { + + if (_is_number(c)) { + exp_beg = true; + + } else if ((c == '-' || c == '+') && !exp_sign && !exp_beg) { + if (c == '-') + is_float = true; + exp_sign = true; + + } else { + reading = READING_DONE; + } + } break; + } + + if (reading == READING_DONE) + break; + num += String::chr(c); + c = GET_CHAR(); + } + + str_ofs--; + + r_token.type = TK_CONSTANT; + + if (is_float) + r_token.value = num.to_double(); + else + r_token.value = num.to_int(); + return OK; + + } else if ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_') { + + String id; + bool first = true; + + while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && _is_number(cchar))) { + + id += String::chr(cchar); + cchar = GET_CHAR(); + first = false; + } + + str_ofs--; //go back one + + if (id == "in") { + r_token.type = TK_OP_IN; + } else if (id == "null") { + r_token.type = TK_CONSTANT; + r_token.value = Variant(); + } else if (id == "true") { + r_token.type = TK_CONSTANT; + r_token.value = true; + } else if (id == "false") { + r_token.type = TK_CONSTANT; + r_token.value = false; + } else if (id == "PI") { + r_token.type = TK_CONSTANT; + r_token.value = Math_PI; + } else if (id == "TAU") { + r_token.type = TK_CONSTANT; + r_token.value = Math_TAU; + } else if (id == "INF") { + r_token.type = TK_CONSTANT; + r_token.value = Math_INF; + } else if (id == "NAN") { + r_token.type = TK_CONSTANT; + r_token.value = Math_NAN; + } else if (id == "not") { + r_token.type = TK_OP_NOT; + } else if (id == "or") { + r_token.type = TK_OP_OR; + } else if (id == "and") { + r_token.type = TK_OP_AND; + } else if (id == "self") { + r_token.type = TK_SELF; + } else { + + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + if (id == Variant::get_type_name(Variant::Type(i))) { + r_token.type = TK_BASIC_TYPE; + r_token.value = i; + return OK; + } + } + + BuiltinFunc bifunc = find_function(id); + if (bifunc != FUNC_MAX) { + r_token.type = TK_BUILTIN_FUNC; + r_token.value = bifunc; + return OK; + } + + r_token.type = TK_IDENTIFIER; + r_token.value = id; + } + + return OK; + + } else if (cchar == '.') { + // Handled down there as we support '.[0-9]' as numbers above + r_token.type = TK_PERIOD; + return OK; + + } else { + _set_error("Unexpected character."); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + } + } +#undef GET_CHAR + } + + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; +} + +const char *Expression::token_name[TK_MAX] = { + "CURLY BRACKET OPEN", + "CURLY BRACKET CLOSE", + "BRACKET OPEN", + "BRACKET CLOSE", + "PARENTHESIS OPEN", + "PARENTHESIS CLOSE", + "IDENTIFIER", + "BUILTIN FUNC", + "SELF", + "CONSTANT", + "BASIC TYPE", + "COLON", + "COMMA", + "PERIOD", + "OP IN", + "OP EQUAL", + "OP NOT EQUAL", + "OP LESS", + "OP LESS EQUAL", + "OP GREATER", + "OP GREATER EQUAL", + "OP AND", + "OP OR", + "OP NOT", + "OP ADD", + "OP SUB", + "OP MUL", + "OP DIV", + "OP MOD", + "OP SHIFT LEFT", + "OP SHIFT RIGHT", + "OP BIT AND", + "OP BIT OR", + "OP BIT XOR", + "OP BIT INVERT", + "OP INPUT", + "EOF", + "ERROR" +}; + +Expression::ENode *Expression::_parse_expression() { + + Vector<ExpressionNode> expression; + + while (true) { + //keep appending stuff to expression + ENode *expr = NULL; + + Token tk; + _get_token(tk); + if (error_set) + return NULL; + + switch (tk.type) { + case TK_CURLY_BRACKET_OPEN: { + //a dictionary + DictionaryNode *dn = alloc_node<DictionaryNode>(); + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_CURLY_BRACKET_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + dn->dict.push_back(expr); + + _get_token(tk); + if (tk.type != TK_COLON) { + _set_error("Expected ':'"); + return NULL; + } + + expr = _parse_expression(); + if (!expr) + return NULL; + + dn->dict.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_CURLY_BRACKET_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or '}'"); + } + } + + expr = dn; + } break; + case TK_BRACKET_OPEN: { + //an array + + ArrayNode *an = alloc_node<ArrayNode>(); + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_BRACKET_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + an->array.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_BRACKET_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ']'"); + } + } + + expr = an; + } break; + case TK_PARENTHESIS_OPEN: { + //a suexpression + ENode *e = _parse_expression(); + if (error_set) + return NULL; + _get_token(tk); + if (tk.type != TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')'"); + return NULL; + } + + expr = e; + + } break; + case TK_IDENTIFIER: { + + String identifier = tk.value; + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_OPEN) { + //function call + CallNode *func_call = alloc_node<CallNode>(); + func_call->method = identifier; + SelfNode *self_node = alloc_node<SelfNode>(); + func_call->base = self_node; + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + + func_call->arguments.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_PARENTHESIS_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + expr = func_call; + } else { + //named indexing + str_ofs = cofs; + + int input_index = -1; + for (int i = 0; i < input_names.size(); i++) { + if (input_names[i] == identifier) { + input_index = i; + break; + } + } + + if (input_index != -1) { + InputNode *input = alloc_node<InputNode>(); + input->index = input_index; + expr = input; + } else { + + NamedIndexNode *index = alloc_node<NamedIndexNode>(); + SelfNode *self_node = alloc_node<SelfNode>(); + index->base = self_node; + index->name = identifier; + expr = index; + } + } + } break; + case TK_INPUT: { + + InputNode *input = alloc_node<InputNode>(); + input->index = tk.value; + expr = input; + } break; + case TK_SELF: { + + SelfNode *self = alloc_node<SelfNode>(); + expr = self; + } break; + case TK_CONSTANT: { + ConstantNode *constant = alloc_node<ConstantNode>(); + constant->value = tk.value; + expr = constant; + } break; + case TK_BASIC_TYPE: { + //constructor.. + + Variant::Type bt = Variant::Type(int(tk.value)); + _get_token(tk); + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '('"); + return NULL; + } + + ConstructorNode *constructor = alloc_node<ConstructorNode>(); + constructor->data_type = bt; + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + + constructor->arguments.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_PARENTHESIS_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + expr = constructor; + + } break; + case TK_BUILTIN_FUNC: { + //builtin function + + _get_token(tk); + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '('"); + return NULL; + } + + BuiltinFuncNode *bifunc = alloc_node<BuiltinFuncNode>(); + bifunc->func = BuiltinFunc(int(tk.value)); + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + + bifunc->arguments.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_PARENTHESIS_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + int expected_args = get_func_argument_count(bifunc->func); + if (bifunc->arguments.size() != expected_args) { + _set_error("Builtin func '" + get_func_name(bifunc->func) + "' expects " + itos(expected_args) + " arguments."); + } + + expr = bifunc; + + } break; + case TK_OP_SUB: { + + ExpressionNode e; + e.is_op = true; + e.op = Variant::OP_NEGATE; + expression.push_back(e); + continue; + } break; + case TK_OP_NOT: { + + ExpressionNode e; + e.is_op = true; + e.op = Variant::OP_NOT; + expression.push_back(e); + continue; + } break; + + default: { + _set_error("Expected expression."); + return NULL; + } break; + } + + //before going to operators, must check indexing! + + while (true) { + int cofs2 = str_ofs; + _get_token(tk); + if (error_set) + return NULL; + + bool done = false; + + switch (tk.type) { + case TK_BRACKET_OPEN: { + //value indexing + + IndexNode *index = alloc_node<IndexNode>(); + index->base = expr; + + ENode *what = _parse_expression(); + if (!what) + return NULL; + + index->index = what; + + _get_token(tk); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']' at end of index."); + return NULL; + } + expr = index; + + } break; + case TK_PERIOD: { + //named indexing or function call + _get_token(tk); + if (tk.type != TK_IDENTIFIER) { + _set_error("Expected identifier after '.'"); + return NULL; + } + + StringName identifier = tk.value; + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_OPEN) { + //function call + CallNode *func_call = alloc_node<CallNode>(); + func_call->method = identifier; + func_call->base = expr; + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + + func_call->arguments.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_PARENTHESIS_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + expr = func_call; + } else { + //named indexing + str_ofs = cofs; + + NamedIndexNode *index = alloc_node<NamedIndexNode>(); + index->base = expr; + index->name = identifier; + expr = index; + } + + } break; + default: { + str_ofs = cofs2; + done = true; + } break; + } + + if (done) + break; + } + + //push expression + { + ExpressionNode e; + e.is_op = false; + e.node = expr; + expression.push_back(e); + } + + //ok finally look for an operator + + int cofs = str_ofs; + _get_token(tk); + if (error_set) + return NULL; + + Variant::Operator op = Variant::OP_MAX; + + switch (tk.type) { + case TK_OP_IN: op = Variant::OP_IN; break; + case TK_OP_EQUAL: op = Variant::OP_EQUAL; break; + case TK_OP_NOT_EQUAL: op = Variant::OP_NOT_EQUAL; break; + case TK_OP_LESS: op = Variant::OP_LESS; break; + case TK_OP_LESS_EQUAL: op = Variant::OP_LESS_EQUAL; break; + case TK_OP_GREATER: op = Variant::OP_GREATER; break; + case TK_OP_GREATER_EQUAL: op = Variant::OP_GREATER_EQUAL; break; + case TK_OP_AND: op = Variant::OP_AND; break; + case TK_OP_OR: op = Variant::OP_OR; break; + case TK_OP_NOT: op = Variant::OP_NOT; break; + case TK_OP_ADD: op = Variant::OP_ADD; break; + case TK_OP_SUB: op = Variant::OP_SUBTRACT; break; + case TK_OP_MUL: op = Variant::OP_MULTIPLY; break; + case TK_OP_DIV: op = Variant::OP_DIVIDE; break; + case TK_OP_MOD: op = Variant::OP_MODULE; break; + case TK_OP_SHIFT_LEFT: op = Variant::OP_SHIFT_LEFT; break; + case TK_OP_SHIFT_RIGHT: op = Variant::OP_SHIFT_RIGHT; break; + case TK_OP_BIT_AND: op = Variant::OP_BIT_AND; break; + case TK_OP_BIT_OR: op = Variant::OP_BIT_OR; break; + case TK_OP_BIT_XOR: op = Variant::OP_BIT_XOR; break; + case TK_OP_BIT_INVERT: op = Variant::OP_BIT_NEGATE; break; + default: {}; + } + + if (op == Variant::OP_MAX) { //stop appending stuff + str_ofs = cofs; + break; + } + + //push operator and go on + { + ExpressionNode e; + e.is_op = true; + e.op = op; + expression.push_back(e); + } + } + + /* Reduce the set set of expressions and place them in an operator tree, respecting precedence */ + + while (expression.size() > 1) { + + int next_op = -1; + int min_priority = 0xFFFFF; + bool is_unary = false; + + for (int i = 0; i < expression.size(); i++) { + + if (!expression[i].is_op) { + + continue; + } + + int priority; + + bool unary = false; + + switch (expression[i].op) { + + case Variant::OP_BIT_NEGATE: + priority = 0; + unary = true; + break; + case Variant::OP_NEGATE: + priority = 1; + unary = true; + break; + + case Variant::OP_MULTIPLY: priority = 2; break; + case Variant::OP_DIVIDE: priority = 2; break; + case Variant::OP_MODULE: priority = 2; break; + + case Variant::OP_ADD: priority = 3; break; + case Variant::OP_SUBTRACT: priority = 3; break; + + case Variant::OP_SHIFT_LEFT: priority = 4; break; + case Variant::OP_SHIFT_RIGHT: priority = 4; break; + + case Variant::OP_BIT_AND: priority = 5; break; + case Variant::OP_BIT_XOR: priority = 6; break; + case Variant::OP_BIT_OR: priority = 7; break; + + case Variant::OP_LESS: priority = 8; break; + case Variant::OP_LESS_EQUAL: priority = 8; break; + case Variant::OP_GREATER: priority = 8; break; + case Variant::OP_GREATER_EQUAL: priority = 8; break; + + case Variant::OP_EQUAL: priority = 8; break; + case Variant::OP_NOT_EQUAL: priority = 8; break; + + case Variant::OP_IN: priority = 10; break; + + case Variant::OP_NOT: + priority = 11; + unary = true; + break; + case Variant::OP_AND: priority = 12; break; + case Variant::OP_OR: priority = 13; break; + + default: { + _set_error("Parser bug, invalid operator in expression: " + itos(expression[i].op)); + return NULL; + } + } + + if (priority < min_priority) { + // < is used for left to right (default) + // <= is used for right to left + + next_op = i; + min_priority = priority; + is_unary = unary; + } + } + + if (next_op == -1) { + + _set_error("Yet another parser bug...."); + ERR_FAIL_COND_V(next_op == -1, NULL); + } + + // OK! create operator.. + if (is_unary) { + + int expr_pos = next_op; + while (expression[expr_pos].is_op) { + + expr_pos++; + if (expr_pos == expression.size()) { + //can happen.. + _set_error("Unexpected end of expression..."); + return NULL; + } + } + + //consecutively do unary opeators + for (int i = expr_pos - 1; i >= next_op; i--) { + + OperatorNode *op = alloc_node<OperatorNode>(); + op->op = expression[i].op; + op->nodes[0] = expression[i + 1].node; + op->nodes[1] = NULL; + expression.write[i].is_op = false; + expression.write[i].node = op; + expression.remove(i + 1); + } + + } else { + + if (next_op < 1 || next_op >= (expression.size() - 1)) { + _set_error("Parser bug..."); + ERR_FAIL_V(NULL); + } + + OperatorNode *op = alloc_node<OperatorNode>(); + op->op = expression[next_op].op; + + if (expression[next_op - 1].is_op) { + + _set_error("Parser bug..."); + ERR_FAIL_V(NULL); + } + + if (expression[next_op + 1].is_op) { + // this is not invalid and can really appear + // but it becomes invalid anyway because no binary op + // can be followed by a unary op in a valid combination, + // due to how precedence works, unaries will always disappear first + + _set_error("Unexpected two consecutive operators."); + return NULL; + } + + op->nodes[0] = expression[next_op - 1].node; //expression goes as left + op->nodes[1] = expression[next_op + 1].node; //next expression goes as right + + //replace all 3 nodes by this operator and make it an expression + expression.write[next_op - 1].node = op; + expression.remove(next_op); + expression.remove(next_op); + } + } + + return expression[0].node; +} + +bool Expression::_compile_expression() { + + if (!expression_dirty) + return error_set; + + if (nodes) { + memdelete(nodes); + nodes = NULL; + root = NULL; + } + + error_str = String(); + error_set = false; + str_ofs = 0; + + root = _parse_expression(); + + if (error_set) { + root = NULL; + if (nodes) { + memdelete(nodes); + } + nodes = NULL; + return true; + } + + expression_dirty = false; + return false; +} + +bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, String &r_error_str) { + + switch (p_node->type) { + case Expression::ENode::TYPE_INPUT: { + + const Expression::InputNode *in = static_cast<const Expression::InputNode *>(p_node); + if (in->index < 0 || in->index >= p_inputs.size()) { + r_error_str = vformat(RTR("Invalid input %i (not passed) in expression"), in->index); + return true; + } + r_ret = p_inputs[in->index]; + } break; + case Expression::ENode::TYPE_CONSTANT: { + + const Expression::ConstantNode *c = static_cast<const Expression::ConstantNode *>(p_node); + r_ret = c->value; + + } break; + case Expression::ENode::TYPE_SELF: { + + if (!p_instance) { + r_error_str = RTR("self can't be used because instance is null (not passed)"); + return true; + } + r_ret = p_instance; + } break; + case Expression::ENode::TYPE_OPERATOR: { + + const Expression::OperatorNode *op = static_cast<const Expression::OperatorNode *>(p_node); + + Variant a; + bool ret = _execute(p_inputs, p_instance, op->nodes[0], a, r_error_str); + if (ret) + return true; + + Variant b; + + if (op->nodes[1]) { + bool ret = _execute(p_inputs, p_instance, op->nodes[1], b, r_error_str); + if (ret) + return true; + } + + bool valid = true; + Variant::evaluate(op->op, a, b, r_ret, valid); + if (!valid) { + r_error_str = vformat(RTR("Invalid operands to operator %s, %s and %s."), Variant::get_operator_name(op->op), Variant::get_type_name(a.get_type()), Variant::get_type_name(b.get_type())); + return true; + } + + } break; + case Expression::ENode::TYPE_INDEX: { + + const Expression::IndexNode *index = static_cast<const Expression::IndexNode *>(p_node); + + Variant base; + bool ret = _execute(p_inputs, p_instance, index->base, base, r_error_str); + if (ret) + return true; + + Variant idx; + + ret = _execute(p_inputs, p_instance, index->index, idx, r_error_str); + if (ret) + return true; + + bool valid; + r_ret = base.get(idx, &valid); + if (!valid) { + r_error_str = vformat(RTR("Invalid index of type %s for base type %s"), Variant::get_type_name(idx.get_type()), Variant::get_type_name(base.get_type())); + return true; + } + + } break; + case Expression::ENode::TYPE_NAMED_INDEX: { + + const Expression::NamedIndexNode *index = static_cast<const Expression::NamedIndexNode *>(p_node); + + Variant base; + bool ret = _execute(p_inputs, p_instance, index->base, base, r_error_str); + if (ret) + return true; + + bool valid; + r_ret = base.get_named(index->name, &valid); + if (!valid) { + r_error_str = vformat(RTR("Invalid named index '%s' for base type %s"), String(index->name), Variant::get_type_name(base.get_type())); + return true; + } + + } break; + case Expression::ENode::TYPE_ARRAY: { + const Expression::ArrayNode *array = static_cast<const Expression::ArrayNode *>(p_node); + + Array arr; + arr.resize(array->array.size()); + for (int i = 0; i < array->array.size(); i++) { + + Variant value; + bool ret = _execute(p_inputs, p_instance, array->array[i], value, r_error_str); + + if (ret) + return true; + arr[i] = value; + } + + r_ret = arr; + + } break; + case Expression::ENode::TYPE_DICTIONARY: { + const Expression::DictionaryNode *dictionary = static_cast<const Expression::DictionaryNode *>(p_node); + + Dictionary d; + for (int i = 0; i < dictionary->dict.size(); i += 2) { + + Variant key; + bool ret = _execute(p_inputs, p_instance, dictionary->dict[i + 0], key, r_error_str); + + if (ret) + return true; + + Variant value; + ret = _execute(p_inputs, p_instance, dictionary->dict[i + 1], value, r_error_str); + if (ret) + return true; + + d[key] = value; + } + + r_ret = d; + } break; + case Expression::ENode::TYPE_CONSTRUCTOR: { + + const Expression::ConstructorNode *constructor = static_cast<const Expression::ConstructorNode *>(p_node); + + Vector<Variant> arr; + Vector<const Variant *> argp; + arr.resize(constructor->arguments.size()); + argp.resize(constructor->arguments.size()); + + for (int i = 0; i < constructor->arguments.size(); i++) { + + Variant value; + bool ret = _execute(p_inputs, p_instance, constructor->arguments[i], value, r_error_str); + + if (ret) + return true; + arr.write[i] = value; + argp.write[i] = &arr[i]; + } + + Variant::CallError ce; + r_ret = Variant::construct(constructor->data_type, (const Variant **)argp.ptr(), argp.size(), ce); + + if (ce.error != Variant::CallError::CALL_OK) { + r_error_str = vformat(RTR("Invalid arguments to construct '%s'"), Variant::get_type_name(constructor->data_type)); + return true; + } + + } break; + case Expression::ENode::TYPE_BUILTIN_FUNC: { + + const Expression::BuiltinFuncNode *bifunc = static_cast<const Expression::BuiltinFuncNode *>(p_node); + + Vector<Variant> arr; + Vector<const Variant *> argp; + arr.resize(bifunc->arguments.size()); + argp.resize(bifunc->arguments.size()); + + for (int i = 0; i < bifunc->arguments.size(); i++) { + + Variant value; + bool ret = _execute(p_inputs, p_instance, bifunc->arguments[i], value, r_error_str); + if (ret) + return true; + arr.write[i] = value; + argp.write[i] = &arr[i]; + } + + Variant::CallError ce; + exec_func(bifunc->func, (const Variant **)argp.ptr(), &r_ret, ce, r_error_str); + + if (ce.error != Variant::CallError::CALL_OK) { + r_error_str = "Builtin Call Failed. " + r_error_str; + return true; + } + + } break; + case Expression::ENode::TYPE_CALL: { + + const Expression::CallNode *call = static_cast<const Expression::CallNode *>(p_node); + + Variant base; + bool ret = _execute(p_inputs, p_instance, call->base, base, r_error_str); + + if (ret) + return true; + + Vector<Variant> arr; + Vector<const Variant *> argp; + arr.resize(call->arguments.size()); + argp.resize(call->arguments.size()); + + for (int i = 0; i < call->arguments.size(); i++) { + + Variant value; + bool ret = _execute(p_inputs, p_instance, call->arguments[i], value, r_error_str); + + if (ret) + return true; + arr.write[i] = value; + argp.write[i] = &arr[i]; + } + + Variant::CallError ce; + r_ret = base.call(call->method, (const Variant **)argp.ptr(), argp.size(), ce); + + if (ce.error != Variant::CallError::CALL_OK) { + r_error_str = vformat(RTR("On call to '%s':"), String(call->method)); + return true; + } + + } break; + } + return false; +} + +Error Expression::parse(const String &p_expression, const Vector<String> &p_input_names) { + + if (nodes) { + memdelete(nodes); + nodes = NULL; + root = NULL; + } + + error_str = String(); + error_set = false; + str_ofs = 0; + input_names = p_input_names; + + expression = p_expression; + root = _parse_expression(); + + if (error_set) { + root = NULL; + if (nodes) { + memdelete(nodes); + } + nodes = NULL; + return ERR_INVALID_PARAMETER; + } + + return OK; +} + +Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error) { + if (error_set) { + ERR_EXPLAIN("There was previously a parse error: " + error_str); + ERR_FAIL_V(Variant()); + } + + execution_error = false; + Variant output; + String error_txt; + bool err = _execute(p_inputs, p_base, root, output, error_txt); + if (err) { + execution_error = true; + error_str = error_txt; + if (p_show_error) { + ERR_EXPLAIN(error_str); + ERR_FAIL_V(Variant()); + } + } + + return output; +} + +bool Expression::has_execute_failed() const { + return execution_error; +} + +String Expression::get_error_text() const { + return error_str; +} + +void Expression::_bind_methods() { + + ClassDB::bind_method(D_METHOD("parse", "expression", "input_names"), &Expression::parse, DEFVAL(Vector<String>())); + ClassDB::bind_method(D_METHOD("execute", "inputs", "base_instance", "show_error"), &Expression::execute, DEFVAL(Array()), DEFVAL(Variant()), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("has_execute_failed"), &Expression::has_execute_failed); + ClassDB::bind_method(D_METHOD("get_error_text"), &Expression::get_error_text); +} + +Expression::Expression() { + output_type = Variant::NIL; + error_set = true; + root = NULL; + nodes = NULL; + sequenced = false; + execution_error = false; +} + +Expression::~Expression() { + + if (nodes) { + memdelete(nodes); + } +} diff --git a/core/math/expression.h b/core/math/expression.h new file mode 100644 index 0000000000..ac2416d0dd --- /dev/null +++ b/core/math/expression.h @@ -0,0 +1,355 @@ +/*************************************************************************/ +/* expression.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef EXPRESSION_H +#define EXPRESSION_H + +#include "core/reference.h" + +class Expression : public Reference { + GDCLASS(Expression, Reference) +public: + enum BuiltinFunc { + MATH_SIN, + MATH_COS, + MATH_TAN, + MATH_SINH, + MATH_COSH, + MATH_TANH, + MATH_ASIN, + MATH_ACOS, + MATH_ATAN, + MATH_ATAN2, + MATH_SQRT, + MATH_FMOD, + MATH_FPOSMOD, + MATH_FLOOR, + MATH_CEIL, + MATH_ROUND, + MATH_ABS, + MATH_SIGN, + MATH_POW, + MATH_LOG, + MATH_EXP, + MATH_ISNAN, + MATH_ISINF, + MATH_EASE, + MATH_DECIMALS, + MATH_STEPIFY, + MATH_LERP, + MATH_INVERSE_LERP, + MATH_RANGE_LERP, + MATH_DECTIME, + MATH_RANDOMIZE, + MATH_RAND, + MATH_RANDF, + MATH_RANDOM, + MATH_SEED, + MATH_RANDSEED, + MATH_DEG2RAD, + MATH_RAD2DEG, + MATH_LINEAR2DB, + MATH_DB2LINEAR, + MATH_POLAR2CARTESIAN, + MATH_CARTESIAN2POLAR, + MATH_WRAP, + MATH_WRAPF, + LOGIC_MAX, + LOGIC_MIN, + LOGIC_CLAMP, + LOGIC_NEAREST_PO2, + OBJ_WEAKREF, + FUNC_FUNCREF, + TYPE_CONVERT, + TYPE_OF, + TYPE_EXISTS, + TEXT_CHAR, + TEXT_STR, + TEXT_PRINT, + TEXT_PRINTERR, + TEXT_PRINTRAW, + VAR_TO_STR, + STR_TO_VAR, + VAR_TO_BYTES, + BYTES_TO_VAR, + COLORN, + FUNC_MAX + }; + + static int get_func_argument_count(BuiltinFunc p_func); + static String get_func_name(BuiltinFunc p_func); + static void exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant *r_return, Variant::CallError &r_error, String &r_error_str); + static BuiltinFunc find_function(const String &p_string); + +private: + static const char *func_name[FUNC_MAX]; + + struct Input { + + Variant::Type type; + String name; + + Input() { type = Variant::NIL; } + }; + + Vector<Input> inputs; + Variant::Type output_type; + + String expression; + + bool sequenced; + int str_ofs; + bool expression_dirty; + + bool _compile_expression(); + + enum TokenType { + TK_CURLY_BRACKET_OPEN, + TK_CURLY_BRACKET_CLOSE, + TK_BRACKET_OPEN, + TK_BRACKET_CLOSE, + TK_PARENTHESIS_OPEN, + TK_PARENTHESIS_CLOSE, + TK_IDENTIFIER, + TK_BUILTIN_FUNC, + TK_SELF, + TK_CONSTANT, + TK_BASIC_TYPE, + TK_COLON, + TK_COMMA, + TK_PERIOD, + TK_OP_IN, + TK_OP_EQUAL, + TK_OP_NOT_EQUAL, + TK_OP_LESS, + TK_OP_LESS_EQUAL, + TK_OP_GREATER, + TK_OP_GREATER_EQUAL, + TK_OP_AND, + TK_OP_OR, + TK_OP_NOT, + TK_OP_ADD, + TK_OP_SUB, + TK_OP_MUL, + TK_OP_DIV, + TK_OP_MOD, + TK_OP_SHIFT_LEFT, + TK_OP_SHIFT_RIGHT, + TK_OP_BIT_AND, + TK_OP_BIT_OR, + TK_OP_BIT_XOR, + TK_OP_BIT_INVERT, + TK_INPUT, + TK_EOF, + TK_ERROR, + TK_MAX + }; + + static const char *token_name[TK_MAX]; + struct Token { + + TokenType type; + Variant value; + }; + + void _set_error(const String &p_err) { + if (error_set) + return; + error_str = p_err; + error_set = true; + } + + Error _get_token(Token &r_token); + + String error_str; + bool error_set; + + struct ENode { + + enum Type { + TYPE_INPUT, + TYPE_CONSTANT, + TYPE_SELF, + TYPE_OPERATOR, + TYPE_INDEX, + TYPE_NAMED_INDEX, + TYPE_ARRAY, + TYPE_DICTIONARY, + TYPE_CONSTRUCTOR, + TYPE_BUILTIN_FUNC, + TYPE_CALL + }; + + ENode *next; + + Type type; + + ENode() { next = NULL; } + virtual ~ENode() { + if (next) { + memdelete(next); + } + } + }; + + struct ExpressionNode { + + bool is_op; + union { + Variant::Operator op; + ENode *node; + }; + }; + + ENode *_parse_expression(); + + struct InputNode : public ENode { + + int index; + InputNode() { + type = TYPE_INPUT; + } + }; + + struct ConstantNode : public ENode { + + Variant value; + ConstantNode() { + type = TYPE_CONSTANT; + } + }; + + struct OperatorNode : public ENode { + + Variant::Operator op; + + ENode *nodes[2]; + + OperatorNode() { + type = TYPE_OPERATOR; + } + }; + + struct SelfNode : public ENode { + + SelfNode() { + type = TYPE_SELF; + } + }; + + struct IndexNode : public ENode { + ENode *base; + ENode *index; + + IndexNode() { + type = TYPE_INDEX; + } + }; + + struct NamedIndexNode : public ENode { + ENode *base; + StringName name; + + NamedIndexNode() { + type = TYPE_NAMED_INDEX; + } + }; + + struct ConstructorNode : public ENode { + Variant::Type data_type; + Vector<ENode *> arguments; + + ConstructorNode() { + type = TYPE_CONSTRUCTOR; + } + }; + + struct CallNode : public ENode { + ENode *base; + StringName method; + Vector<ENode *> arguments; + + CallNode() { + type = TYPE_CALL; + } + }; + + struct ArrayNode : public ENode { + Vector<ENode *> array; + ArrayNode() { + type = TYPE_ARRAY; + } + }; + + struct DictionaryNode : public ENode { + Vector<ENode *> dict; + DictionaryNode() { + type = TYPE_DICTIONARY; + } + }; + + struct BuiltinFuncNode : public ENode { + BuiltinFunc func; + Vector<ENode *> arguments; + BuiltinFuncNode() { + type = TYPE_BUILTIN_FUNC; + } + }; + + template <class T> + T *alloc_node() { + T *node = memnew(T); + node->next = nodes; + nodes = node; + return node; + } + + ENode *root; + ENode *nodes; + + Vector<String> input_names; + + bool execution_error; + bool _execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, String &r_error_str); + +protected: + static void _bind_methods(); + +public: + Error parse(const String &p_expression, const Vector<String> &p_input_names = Vector<String>()); + Variant execute(Array p_inputs, Object *p_base = NULL, bool p_show_error = true); + bool has_execute_failed() const; + String get_error_text() const; + + Expression(); + ~Expression(); +}; + +#endif // EXPRESSION_H diff --git a/core/math/face3.cpp b/core/math/face3.cpp index 801f2a3b4d..8366137131 100644 --- a/core/math/face3.cpp +++ b/core/math/face3.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "face3.h" -#include "geometry.h" + +#include "core/math/geometry.h" int Face3::split_by_plane(const Plane &p_plane, Face3 p_res[3], bool p_is_point_over[3]) const { @@ -201,11 +202,12 @@ bool Face3::intersects_aabb(const AABB &p_aabb) const { { \ real_t aabb_min = p_aabb.position.m_ax; \ real_t aabb_max = p_aabb.position.m_ax + p_aabb.size.m_ax; \ - real_t tri_min, tri_max; \ - for (int i = 0; i < 3; i++) { \ - if (i == 0 || vertex[i].m_ax > tri_max) \ + real_t tri_min = vertex[0].m_ax; \ + real_t tri_max = vertex[0].m_ax; \ + for (int i = 1; i < 3; i++) { \ + if (vertex[i].m_ax > tri_max) \ tri_max = vertex[i].m_ax; \ - if (i == 0 || vertex[i].m_ax < tri_min) \ + if (vertex[i].m_ax < tri_min) \ tri_min = vertex[i].m_ax; \ } \ \ diff --git a/core/math/face3.h b/core/math/face3.h index faed0fa8d4..b41daf04d4 100644 --- a/core/math/face3.h +++ b/core/math/face3.h @@ -31,10 +31,10 @@ #ifndef FACE3_H #define FACE3_H -#include "aabb.h" -#include "plane.h" -#include "transform.h" -#include "vector3.h" +#include "core/math/aabb.h" +#include "core/math/plane.h" +#include "core/math/transform.h" +#include "core/math/vector3.h" class Face3 { public: diff --git a/core/math/geometry.cpp b/core/math/geometry.cpp index 7ab28daf20..be5e40e4e6 100644 --- a/core/math/geometry.cpp +++ b/core/math/geometry.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "geometry.h" -#include "print_string.h" + +#include "core/print_string.h" bool Geometry::is_point_in_polygon(const Vector2 &p_point, const Vector<Vector2> &p_polygon) { @@ -626,7 +627,6 @@ PoolVector<Face3> Geometry::wrap_geometry(PoolVector<Face3> p_array, real_t *p_e voxelsize.z /= div_z; // create and initialize cells to zero - //print_line("Wrapper: Initializing Cells"); uint8_t ***cell_status = memnew_arr(uint8_t **, div_x); for (int i = 0; i < div_x; i++) { @@ -645,7 +645,6 @@ PoolVector<Face3> Geometry::wrap_geometry(PoolVector<Face3> p_array, real_t *p_e } // plot faces into cells - //print_line("Wrapper (1/6): Plotting Faces"); for (int i = 0; i < face_count; i++) { @@ -659,8 +658,6 @@ PoolVector<Face3> Geometry::wrap_geometry(PoolVector<Face3> p_array, real_t *p_e // determine which cells connect to the outside by traversing the outside and recursively flood-fill marking - //print_line("Wrapper (2/6): Flood Filling"); - for (int i = 0; i < div_x; i++) { for (int j = 0; j < div_y; j++) { @@ -690,8 +687,6 @@ PoolVector<Face3> Geometry::wrap_geometry(PoolVector<Face3> p_array, real_t *p_e // build faces for the inside-outside cell divisors - //print_line("Wrapper (3/6): Building Faces"); - PoolVector<Face3> wrapped_faces; for (int i = 0; i < div_x; i++) { @@ -705,8 +700,6 @@ PoolVector<Face3> Geometry::wrap_geometry(PoolVector<Face3> p_array, real_t *p_e } } - //print_line("Wrapper (4/6): Transforming Back Vertices"); - // transform face vertices to global coords int wrapped_faces_count = wrapped_faces.size(); @@ -724,7 +717,6 @@ PoolVector<Face3> Geometry::wrap_geometry(PoolVector<Face3> p_array, real_t *p_e } // clean up grid - //print_line("Wrapper (5/6): Grid Cleanup"); for (int i = 0; i < div_x; i++) { @@ -740,7 +732,6 @@ PoolVector<Face3> Geometry::wrap_geometry(PoolVector<Face3> p_array, real_t *p_e if (p_error) *p_error = voxelsize.length(); - //print_line("Wrapper (6/6): Finished."); return wrapped_faces; } diff --git a/core/math/geometry.h b/core/math/geometry.h index 186a05fb37..df63f0dabe 100644 --- a/core/math/geometry.h +++ b/core/math/geometry.h @@ -31,14 +31,15 @@ #ifndef GEOMETRY_H #define GEOMETRY_H -#include "dvector.h" -#include "face3.h" -#include "math_2d.h" -#include "object.h" -#include "print_string.h" -#include "triangulate.h" -#include "vector.h" -#include "vector3.h" +#include "core/dvector.h" +#include "core/math/face3.h" +#include "core/math/rect2.h" +#include "core/math/triangulate.h" +#include "core/math/vector3.h" +#include "core/object.h" +#include "core/print_string.h" +#include "core/vector.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -799,6 +800,21 @@ public: return Vector<Vector<Vector2> >(); } + static bool is_polygon_clockwise(const Vector<Vector2> &p_polygon) { + int c = p_polygon.size(); + if (c < 3) + return false; + const Vector2 *p = p_polygon.ptr(); + real_t sum = 0; + for (int i = 0; i < c; i++) { + const Vector2 &v1 = p[i]; + const Vector2 &v2 = p[(i + 1) % c]; + sum += (v2.x - v1.x) * (v2.y + v1.y); + } + + return sum > 0.0f; + } + static PoolVector<PoolVector<Face3> > separate_objects(PoolVector<Face3> p_array); static PoolVector<Face3> wrap_geometry(PoolVector<Face3> p_array, real_t *p_error = NULL); ///< create a "wrap" that encloses the given geometry diff --git a/core/math/math_2d.h b/core/math/math_2d.h deleted file mode 100644 index 25c39e5d7a..0000000000 --- a/core/math/math_2d.h +++ /dev/null @@ -1,1002 +0,0 @@ -/*************************************************************************/ -/* math_2d.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef MATH_2D_H -#define MATH_2D_H - -#include "math_funcs.h" -#include "ustring.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ -enum Margin { - - MARGIN_LEFT, - MARGIN_TOP, - MARGIN_RIGHT, - MARGIN_BOTTOM -}; - -enum Corner { - - CORNER_TOP_LEFT, - CORNER_TOP_RIGHT, - CORNER_BOTTOM_RIGHT, - CORNER_BOTTOM_LEFT -}; - -enum Orientation { - - HORIZONTAL, - VERTICAL -}; - -enum HAlign { - - HALIGN_LEFT, - HALIGN_CENTER, - HALIGN_RIGHT -}; - -enum VAlign { - - VALIGN_TOP, - VALIGN_CENTER, - VALIGN_BOTTOM -}; - -struct Vector2 { - - union { - real_t x; - real_t width; - }; - union { - real_t y; - real_t height; - }; - - _FORCE_INLINE_ real_t &operator[](int p_idx) { - return p_idx ? y : x; - } - _FORCE_INLINE_ const real_t &operator[](int p_idx) const { - return p_idx ? y : x; - } - - void normalize(); - Vector2 normalized() const; - bool is_normalized() const; - - real_t length() const; - real_t length_squared() const; - - real_t distance_to(const Vector2 &p_vector2) const; - real_t distance_squared_to(const Vector2 &p_vector2) const; - real_t angle_to(const Vector2 &p_vector2) const; - real_t angle_to_point(const Vector2 &p_vector2) const; - - real_t dot(const Vector2 &p_other) const; - real_t cross(const Vector2 &p_other) const; - Vector2 project(const Vector2 &p_vec) const; - - Vector2 plane_project(real_t p_d, const Vector2 &p_vec) const; - - Vector2 clamped(real_t p_len) const; - - _FORCE_INLINE_ static Vector2 linear_interpolate(const Vector2 &p_a, const Vector2 &p_b, real_t p_t); - _FORCE_INLINE_ Vector2 linear_interpolate(const Vector2 &p_b, real_t p_t) const; - _FORCE_INLINE_ Vector2 slerp(const Vector2 &p_b, real_t p_t) const; - Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_t) const; - - Vector2 slide(const Vector2 &p_normal) const; - Vector2 bounce(const Vector2 &p_normal) const; - Vector2 reflect(const Vector2 &p_normal) const; - - Vector2 operator+(const Vector2 &p_v) const; - void operator+=(const Vector2 &p_v); - Vector2 operator-(const Vector2 &p_v) const; - void operator-=(const Vector2 &p_v); - Vector2 operator*(const Vector2 &p_v1) const; - - Vector2 operator*(const real_t &rvalue) const; - void operator*=(const real_t &rvalue); - void operator*=(const Vector2 &rvalue) { *this = *this * rvalue; } - - Vector2 operator/(const Vector2 &p_v1) const; - - Vector2 operator/(const real_t &rvalue) const; - - void operator/=(const real_t &rvalue); - - Vector2 operator-() const; - - bool operator==(const Vector2 &p_vec2) const; - bool operator!=(const Vector2 &p_vec2) const; - - bool operator<(const Vector2 &p_vec2) const { return (x == p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); } - bool operator<=(const Vector2 &p_vec2) const { return (x == p_vec2.x) ? (y <= p_vec2.y) : (x <= p_vec2.x); } - - real_t angle() const; - - void set_rotation(real_t p_radians) { - - x = Math::cos(p_radians); - y = Math::sin(p_radians); - } - - _FORCE_INLINE_ Vector2 abs() const { - - return Vector2(Math::abs(x), Math::abs(y)); - } - - Vector2 rotated(real_t p_by) const; - Vector2 tangent() const { - - return Vector2(y, -x); - } - - Vector2 floor() const; - Vector2 ceil() const; - Vector2 round() const; - Vector2 snapped(const Vector2 &p_by) const; - real_t aspect() const { return width / height; } - - operator String() const { return String::num(x) + ", " + String::num(y); } - - _FORCE_INLINE_ Vector2(real_t p_x, real_t p_y) { - x = p_x; - y = p_y; - } - _FORCE_INLINE_ Vector2() { - x = 0; - y = 0; - } -}; - -_FORCE_INLINE_ Vector2 Vector2::plane_project(real_t p_d, const Vector2 &p_vec) const { - - return p_vec - *this * (dot(p_vec) - p_d); -} - -_FORCE_INLINE_ Vector2 operator*(real_t p_scalar, const Vector2 &p_vec) { - - return p_vec * p_scalar; -} - -_FORCE_INLINE_ Vector2 Vector2::operator+(const Vector2 &p_v) const { - - return Vector2(x + p_v.x, y + p_v.y); -} -_FORCE_INLINE_ void Vector2::operator+=(const Vector2 &p_v) { - - x += p_v.x; - y += p_v.y; -} -_FORCE_INLINE_ Vector2 Vector2::operator-(const Vector2 &p_v) const { - - return Vector2(x - p_v.x, y - p_v.y); -} -_FORCE_INLINE_ void Vector2::operator-=(const Vector2 &p_v) { - - x -= p_v.x; - y -= p_v.y; -} - -_FORCE_INLINE_ Vector2 Vector2::operator*(const Vector2 &p_v1) const { - - return Vector2(x * p_v1.x, y * p_v1.y); -}; - -_FORCE_INLINE_ Vector2 Vector2::operator*(const real_t &rvalue) const { - - return Vector2(x * rvalue, y * rvalue); -}; -_FORCE_INLINE_ void Vector2::operator*=(const real_t &rvalue) { - - x *= rvalue; - y *= rvalue; -}; - -_FORCE_INLINE_ Vector2 Vector2::operator/(const Vector2 &p_v1) const { - - return Vector2(x / p_v1.x, y / p_v1.y); -}; - -_FORCE_INLINE_ Vector2 Vector2::operator/(const real_t &rvalue) const { - - return Vector2(x / rvalue, y / rvalue); -}; - -_FORCE_INLINE_ void Vector2::operator/=(const real_t &rvalue) { - - x /= rvalue; - y /= rvalue; -}; - -_FORCE_INLINE_ Vector2 Vector2::operator-() const { - - return Vector2(-x, -y); -} - -_FORCE_INLINE_ bool Vector2::operator==(const Vector2 &p_vec2) const { - - return x == p_vec2.x && y == p_vec2.y; -} -_FORCE_INLINE_ bool Vector2::operator!=(const Vector2 &p_vec2) const { - - return x != p_vec2.x || y != p_vec2.y; -} - -Vector2 Vector2::linear_interpolate(const Vector2 &p_b, real_t p_t) const { - - Vector2 res = *this; - - res.x += (p_t * (p_b.x - x)); - res.y += (p_t * (p_b.y - y)); - - return res; -} - -Vector2 Vector2::slerp(const Vector2 &p_b, real_t p_t) const { -#ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_normalized() == false, Vector2()); -#endif - real_t theta = angle_to(p_b); - return rotated(theta * p_t); -} - -Vector2 Vector2::linear_interpolate(const Vector2 &p_a, const Vector2 &p_b, real_t p_t) { - - Vector2 res = p_a; - - res.x += (p_t * (p_b.x - p_a.x)); - res.y += (p_t * (p_b.y - p_a.y)); - - return res; -} - -typedef Vector2 Size2; -typedef Vector2 Point2; - -struct Transform2D; - -struct Rect2 { - - Point2 position; - Size2 size; - - const Vector2 &get_position() const { return position; } - void set_position(const Vector2 &p_pos) { position = p_pos; } - const Vector2 &get_size() const { return size; } - void set_size(const Vector2 &p_size) { size = p_size; } - - real_t get_area() const { return size.width * size.height; } - - inline bool intersects(const Rect2 &p_rect) const { - if (position.x >= (p_rect.position.x + p_rect.size.width)) - return false; - if ((position.x + size.width) <= p_rect.position.x) - return false; - if (position.y >= (p_rect.position.y + p_rect.size.height)) - return false; - if ((position.y + size.height) <= p_rect.position.y) - return false; - - return true; - } - - inline real_t distance_to(const Vector2 &p_point) const { - - real_t dist = 0.0; - bool inside = true; - - if (p_point.x < position.x) { - real_t d = position.x - p_point.x; - dist = inside ? d : MIN(dist, d); - inside = false; - } - if (p_point.y < position.y) { - real_t d = position.y - p_point.y; - dist = inside ? d : MIN(dist, d); - inside = false; - } - if (p_point.x >= (position.x + size.x)) { - real_t d = p_point.x - (position.x + size.x); - dist = inside ? d : MIN(dist, d); - inside = false; - } - if (p_point.y >= (position.y + size.y)) { - real_t d = p_point.y - (position.y + size.y); - dist = inside ? d : MIN(dist, d); - inside = false; - } - - if (inside) - return 0; - else - return dist; - } - - _FORCE_INLINE_ bool intersects_transformed(const Transform2D &p_xform, const Rect2 &p_rect) const; - - bool intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos = NULL, Point2 *r_normal = NULL) const; - - inline bool encloses(const Rect2 &p_rect) const { - - return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && - ((p_rect.position.x + p_rect.size.x) < (position.x + size.x)) && - ((p_rect.position.y + p_rect.size.y) < (position.y + size.y)); - } - - inline bool has_no_area() const { - - return (size.x <= 0 || size.y <= 0); - } - inline Rect2 clip(const Rect2 &p_rect) const { /// return a clipped rect - - Rect2 new_rect = p_rect; - - if (!intersects(new_rect)) - return Rect2(); - - new_rect.position.x = MAX(p_rect.position.x, position.x); - new_rect.position.y = MAX(p_rect.position.y, position.y); - - Point2 p_rect_end = p_rect.position + p_rect.size; - Point2 end = position + size; - - new_rect.size.x = MIN(p_rect_end.x, end.x) - new_rect.position.x; - new_rect.size.y = MIN(p_rect_end.y, end.y) - new_rect.position.y; - - return new_rect; - } - - inline Rect2 merge(const Rect2 &p_rect) const { ///< return a merged rect - - Rect2 new_rect; - - new_rect.position.x = MIN(p_rect.position.x, position.x); - new_rect.position.y = MIN(p_rect.position.y, position.y); - - new_rect.size.x = MAX(p_rect.position.x + p_rect.size.x, position.x + size.x); - new_rect.size.y = MAX(p_rect.position.y + p_rect.size.y, position.y + size.y); - - new_rect.size = new_rect.size - new_rect.position; //make relative again - - return new_rect; - }; - inline bool has_point(const Point2 &p_point) const { - if (p_point.x < position.x) - return false; - if (p_point.y < position.y) - return false; - - if (p_point.x >= (position.x + size.x)) - return false; - if (p_point.y >= (position.y + size.y)) - return false; - - return true; - } - - inline bool no_area() const { return (size.width <= 0 || size.height <= 0); } - - bool operator==(const Rect2 &p_rect) const { return position == p_rect.position && size == p_rect.size; } - bool operator!=(const Rect2 &p_rect) const { return position != p_rect.position || size != p_rect.size; } - - inline Rect2 grow(real_t p_by) const { - - Rect2 g = *this; - g.position.x -= p_by; - g.position.y -= p_by; - g.size.width += p_by * 2; - g.size.height += p_by * 2; - return g; - } - - inline Rect2 grow_margin(Margin p_margin, real_t p_amount) const { - Rect2 g = *this; - g = g.grow_individual((MARGIN_LEFT == p_margin) ? p_amount : 0, - (MARGIN_TOP == p_margin) ? p_amount : 0, - (MARGIN_RIGHT == p_margin) ? p_amount : 0, - (MARGIN_BOTTOM == p_margin) ? p_amount : 0); - return g; - } - - inline Rect2 grow_individual(real_t p_left, real_t p_top, real_t p_right, real_t p_bottom) const { - - Rect2 g = *this; - g.position.x -= p_left; - g.position.y -= p_top; - g.size.width += p_left + p_right; - g.size.height += p_top + p_bottom; - - return g; - } - - inline Rect2 expand(const Vector2 &p_vector) const { - - Rect2 r = *this; - r.expand_to(p_vector); - return r; - } - - inline void expand_to(const Vector2 &p_vector) { //in place function for speed - - Vector2 begin = position; - Vector2 end = position + size; - - if (p_vector.x < begin.x) - begin.x = p_vector.x; - if (p_vector.y < begin.y) - begin.y = p_vector.y; - - if (p_vector.x > end.x) - end.x = p_vector.x; - if (p_vector.y > end.y) - end.y = p_vector.y; - - position = begin; - size = end - begin; - } - - inline Rect2 abs() const { - - return Rect2(Point2(position.x + MIN(size.x, 0), position.y + MIN(size.y, 0)), size.abs()); - } - - operator String() const { return String(position) + ", " + String(size); } - - Rect2() {} - Rect2(real_t p_x, real_t p_y, real_t p_width, real_t p_height) : - position(Point2(p_x, p_y)), - size(Size2(p_width, p_height)) { - } - Rect2(const Point2 &p_pos, const Size2 &p_size) : - position(p_pos), - size(p_size) { - } -}; - -/* INTEGER STUFF */ - -struct Point2i { - - union { - int x; - int width; - }; - union { - int y; - int height; - }; - - _FORCE_INLINE_ int &operator[](int p_idx) { - return p_idx ? y : x; - } - _FORCE_INLINE_ const int &operator[](int p_idx) const { - return p_idx ? y : x; - } - - Point2i operator+(const Point2i &p_v) const; - void operator+=(const Point2i &p_v); - Point2i operator-(const Point2i &p_v) const; - void operator-=(const Point2i &p_v); - Point2i operator*(const Point2i &p_v1) const; - - Point2i operator*(const int &rvalue) const; - void operator*=(const int &rvalue); - - Point2i operator/(const Point2i &p_v1) const; - - Point2i operator/(const int &rvalue) const; - - void operator/=(const int &rvalue); - - Point2i operator-() const; - bool operator<(const Point2i &p_vec2) const { return (x == p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); } - bool operator>(const Point2i &p_vec2) const { return (x == p_vec2.x) ? (y > p_vec2.y) : (x > p_vec2.x); } - - bool operator==(const Point2i &p_vec2) const; - bool operator!=(const Point2i &p_vec2) const; - - real_t get_aspect() const { return width / (real_t)height; } - - operator String() const { return String::num(x) + ", " + String::num(y); } - - operator Vector2() const { return Vector2(x, y); } - inline Point2i(const Vector2 &p_vec2) { - x = (int)p_vec2.x; - y = (int)p_vec2.y; - } - inline Point2i(int p_x, int p_y) { - x = p_x; - y = p_y; - } - inline Point2i() { - x = 0; - y = 0; - } -}; - -typedef Point2i Size2i; - -struct Rect2i { - - Point2i position; - Size2i size; - - const Point2i &get_position() const { return position; } - void set_position(const Point2i &p_pos) { position = p_pos; } - const Point2i &get_size() const { return size; } - void set_size(const Point2i &p_size) { size = p_size; } - - int get_area() const { return size.width * size.height; } - - inline bool intersects(const Rect2i &p_rect) const { - if (position.x > (p_rect.position.x + p_rect.size.width)) - return false; - if ((position.x + size.width) < p_rect.position.x) - return false; - if (position.y > (p_rect.position.y + p_rect.size.height)) - return false; - if ((position.y + size.height) < p_rect.position.y) - return false; - - return true; - } - - inline bool encloses(const Rect2i &p_rect) const { - - return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && - ((p_rect.position.x + p_rect.size.x) < (position.x + size.x)) && - ((p_rect.position.y + p_rect.size.y) < (position.y + size.y)); - } - - inline bool has_no_area() const { - - return (size.x <= 0 || size.y <= 0); - } - inline Rect2i clip(const Rect2i &p_rect) const { /// return a clipped rect - - Rect2i new_rect = p_rect; - - if (!intersects(new_rect)) - return Rect2i(); - - new_rect.position.x = MAX(p_rect.position.x, position.x); - new_rect.position.y = MAX(p_rect.position.y, position.y); - - Point2 p_rect_end = p_rect.position + p_rect.size; - Point2 end = position + size; - - new_rect.size.x = (int)(MIN(p_rect_end.x, end.x) - new_rect.position.x); - new_rect.size.y = (int)(MIN(p_rect_end.y, end.y) - new_rect.position.y); - - return new_rect; - } - - inline Rect2i merge(const Rect2i &p_rect) const { ///< return a merged rect - - Rect2i new_rect; - - new_rect.position.x = MIN(p_rect.position.x, position.x); - new_rect.position.y = MIN(p_rect.position.y, position.y); - - new_rect.size.x = MAX(p_rect.position.x + p_rect.size.x, position.x + size.x); - new_rect.size.y = MAX(p_rect.position.y + p_rect.size.y, position.y + size.y); - - new_rect.size = new_rect.size - new_rect.position; //make relative again - - return new_rect; - }; - bool has_point(const Point2 &p_point) const { - if (p_point.x < position.x) - return false; - if (p_point.y < position.y) - return false; - - if (p_point.x >= (position.x + size.x)) - return false; - if (p_point.y >= (position.y + size.y)) - return false; - - return true; - } - - bool no_area() { return (size.width <= 0 || size.height <= 0); } - - bool operator==(const Rect2i &p_rect) const { return position == p_rect.position && size == p_rect.size; } - bool operator!=(const Rect2i &p_rect) const { return position != p_rect.position || size != p_rect.size; } - - Rect2i grow(int p_by) const { - - Rect2i g = *this; - g.position.x -= p_by; - g.position.y -= p_by; - g.size.width += p_by * 2; - g.size.height += p_by * 2; - return g; - } - - inline void expand_to(const Point2i &p_vector) { - - Point2i begin = position; - Point2i end = position + size; - - if (p_vector.x < begin.x) - begin.x = p_vector.x; - if (p_vector.y < begin.y) - begin.y = p_vector.y; - - if (p_vector.x > end.x) - end.x = p_vector.x; - if (p_vector.y > end.y) - end.y = p_vector.y; - - position = begin; - size = end - begin; - } - - operator String() const { return String(position) + ", " + String(size); } - - operator Rect2() const { return Rect2(position, size); } - Rect2i(const Rect2 &p_r2) : - position(p_r2.position), - size(p_r2.size) { - } - Rect2i() {} - Rect2i(int p_x, int p_y, int p_width, int p_height) : - position(Point2(p_x, p_y)), - size(Size2(p_width, p_height)) { - } - Rect2i(const Point2 &p_pos, const Size2 &p_size) : - position(p_pos), - size(p_size) { - } -}; - -struct Transform2D { - // Warning #1: basis of Transform2D is stored differently from Basis. In terms of elements array, the basis matrix looks like "on paper": - // M = (elements[0][0] elements[1][0]) - // (elements[0][1] elements[1][1]) - // This is such that the columns, which can be interpreted as basis vectors of the coordinate system "painted" on the object, can be accessed as elements[i]. - // Note that this is the opposite of the indices in mathematical texts, meaning: $M_{12}$ in a math book corresponds to elements[1][0] here. - // This requires additional care when working with explicit indices. - // See https://en.wikipedia.org/wiki/Row-_and_column-major_order for further reading. - - // Warning #2: 2D be aware that unlike 3D code, 2D code uses a left-handed coordinate system: Y-axis points down, - // and angle is measure from +X to +Y in a clockwise-fashion. - - Vector2 elements[3]; - - _FORCE_INLINE_ real_t tdotx(const Vector2 &v) const { return elements[0][0] * v.x + elements[1][0] * v.y; } - _FORCE_INLINE_ real_t tdoty(const Vector2 &v) const { return elements[0][1] * v.x + elements[1][1] * v.y; } - - const Vector2 &operator[](int p_idx) const { return elements[p_idx]; } - Vector2 &operator[](int p_idx) { return elements[p_idx]; } - - _FORCE_INLINE_ Vector2 get_axis(int p_axis) const { - ERR_FAIL_INDEX_V(p_axis, 3, Vector2()); - return elements[p_axis]; - } - _FORCE_INLINE_ void set_axis(int p_axis, const Vector2 &p_vec) { - ERR_FAIL_INDEX(p_axis, 3); - elements[p_axis] = p_vec; - } - - void invert(); - Transform2D inverse() const; - - void affine_invert(); - Transform2D affine_inverse() const; - - void set_rotation(real_t p_rot); - real_t get_rotation() const; - _FORCE_INLINE_ void set_rotation_and_scale(real_t p_rot, const Size2 &p_scale); - void rotate(real_t p_phi); - - void scale(const Size2 &p_scale); - void scale_basis(const Size2 &p_scale); - void translate(real_t p_tx, real_t p_ty); - void translate(const Vector2 &p_translation); - - real_t basis_determinant() const; - - Size2 get_scale() const; - - _FORCE_INLINE_ const Vector2 &get_origin() const { return elements[2]; } - _FORCE_INLINE_ void set_origin(const Vector2 &p_origin) { elements[2] = p_origin; } - - Transform2D scaled(const Size2 &p_scale) const; - Transform2D basis_scaled(const Size2 &p_scale) const; - Transform2D translated(const Vector2 &p_offset) const; - Transform2D rotated(real_t p_phi) const; - - Transform2D untranslated() const; - - void orthonormalize(); - Transform2D orthonormalized() const; - - bool operator==(const Transform2D &p_transform) const; - bool operator!=(const Transform2D &p_transform) const; - - void operator*=(const Transform2D &p_transform); - Transform2D operator*(const Transform2D &p_transform) const; - - Transform2D interpolate_with(const Transform2D &p_transform, real_t p_c) const; - - _FORCE_INLINE_ Vector2 basis_xform(const Vector2 &p_vec) const; - _FORCE_INLINE_ Vector2 basis_xform_inv(const Vector2 &p_vec) const; - _FORCE_INLINE_ Vector2 xform(const Vector2 &p_vec) const; - _FORCE_INLINE_ Vector2 xform_inv(const Vector2 &p_vec) const; - _FORCE_INLINE_ Rect2 xform(const Rect2 &p_rect) const; - _FORCE_INLINE_ Rect2 xform_inv(const Rect2 &p_rect) const; - - operator String() const; - - Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy) { - - elements[0][0] = xx; - elements[0][1] = xy; - elements[1][0] = yx; - elements[1][1] = yy; - elements[2][0] = ox; - elements[2][1] = oy; - } - - Transform2D(real_t p_rot, const Vector2 &p_pos); - Transform2D() { - elements[0][0] = 1.0; - elements[1][1] = 1.0; - } -}; - -bool Rect2::intersects_transformed(const Transform2D &p_xform, const Rect2 &p_rect) const { - - //SAT intersection between local and transformed rect2 - - Vector2 xf_points[4] = { - p_xform.xform(p_rect.position), - p_xform.xform(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y)), - p_xform.xform(Vector2(p_rect.position.x, p_rect.position.y + p_rect.size.y)), - p_xform.xform(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y + p_rect.size.y)), - }; - - real_t low_limit; - - //base rect2 first (faster) - - if (xf_points[0].y > position.y) - goto next1; - if (xf_points[1].y > position.y) - goto next1; - if (xf_points[2].y > position.y) - goto next1; - if (xf_points[3].y > position.y) - goto next1; - - return false; - -next1: - - low_limit = position.y + size.y; - - if (xf_points[0].y < low_limit) - goto next2; - if (xf_points[1].y < low_limit) - goto next2; - if (xf_points[2].y < low_limit) - goto next2; - if (xf_points[3].y < low_limit) - goto next2; - - return false; - -next2: - - if (xf_points[0].x > position.x) - goto next3; - if (xf_points[1].x > position.x) - goto next3; - if (xf_points[2].x > position.x) - goto next3; - if (xf_points[3].x > position.x) - goto next3; - - return false; - -next3: - - low_limit = position.x + size.x; - - if (xf_points[0].x < low_limit) - goto next4; - if (xf_points[1].x < low_limit) - goto next4; - if (xf_points[2].x < low_limit) - goto next4; - if (xf_points[3].x < low_limit) - goto next4; - - return false; - -next4: - - Vector2 xf_points2[4] = { - position, - Vector2(position.x + size.x, position.y), - Vector2(position.x, position.y + size.y), - Vector2(position.x + size.x, position.y + size.y), - }; - - real_t maxa = p_xform.elements[0].dot(xf_points2[0]); - real_t mina = maxa; - - real_t dp = p_xform.elements[0].dot(xf_points2[1]); - maxa = MAX(dp, maxa); - mina = MIN(dp, mina); - - dp = p_xform.elements[0].dot(xf_points2[2]); - maxa = MAX(dp, maxa); - mina = MIN(dp, mina); - - dp = p_xform.elements[0].dot(xf_points2[3]); - maxa = MAX(dp, maxa); - mina = MIN(dp, mina); - - real_t maxb = p_xform.elements[0].dot(xf_points[0]); - real_t minb = maxb; - - dp = p_xform.elements[0].dot(xf_points[1]); - maxb = MAX(dp, maxb); - minb = MIN(dp, minb); - - dp = p_xform.elements[0].dot(xf_points[2]); - maxb = MAX(dp, maxb); - minb = MIN(dp, minb); - - dp = p_xform.elements[0].dot(xf_points[3]); - maxb = MAX(dp, maxb); - minb = MIN(dp, minb); - - if (mina > maxb) - return false; - if (minb > maxa) - return false; - - maxa = p_xform.elements[1].dot(xf_points2[0]); - mina = maxa; - - dp = p_xform.elements[1].dot(xf_points2[1]); - maxa = MAX(dp, maxa); - mina = MIN(dp, mina); - - dp = p_xform.elements[1].dot(xf_points2[2]); - maxa = MAX(dp, maxa); - mina = MIN(dp, mina); - - dp = p_xform.elements[1].dot(xf_points2[3]); - maxa = MAX(dp, maxa); - mina = MIN(dp, mina); - - maxb = p_xform.elements[1].dot(xf_points[0]); - minb = maxb; - - dp = p_xform.elements[1].dot(xf_points[1]); - maxb = MAX(dp, maxb); - minb = MIN(dp, minb); - - dp = p_xform.elements[1].dot(xf_points[2]); - maxb = MAX(dp, maxb); - minb = MIN(dp, minb); - - dp = p_xform.elements[1].dot(xf_points[3]); - maxb = MAX(dp, maxb); - minb = MIN(dp, minb); - - if (mina > maxb) - return false; - if (minb > maxa) - return false; - - return true; -} - -Vector2 Transform2D::basis_xform(const Vector2 &p_vec) const { - - return Vector2( - tdotx(p_vec), - tdoty(p_vec)); -} - -Vector2 Transform2D::basis_xform_inv(const Vector2 &p_vec) const { - - return Vector2( - elements[0].dot(p_vec), - elements[1].dot(p_vec)); -} - -Vector2 Transform2D::xform(const Vector2 &p_vec) const { - - return Vector2( - tdotx(p_vec), - tdoty(p_vec)) + - elements[2]; -} -Vector2 Transform2D::xform_inv(const Vector2 &p_vec) const { - - Vector2 v = p_vec - elements[2]; - - return Vector2( - elements[0].dot(v), - elements[1].dot(v)); -} -Rect2 Transform2D::xform(const Rect2 &p_rect) const { - - Vector2 x = elements[0] * p_rect.size.x; - Vector2 y = elements[1] * p_rect.size.y; - Vector2 pos = xform(p_rect.position); - - Rect2 new_rect; - new_rect.position = pos; - new_rect.expand_to(pos + x); - new_rect.expand_to(pos + y); - new_rect.expand_to(pos + x + y); - return new_rect; -} - -void Transform2D::set_rotation_and_scale(real_t p_rot, const Size2 &p_scale) { - - elements[0][0] = Math::cos(p_rot) * p_scale.x; - elements[1][1] = Math::cos(p_rot) * p_scale.y; - elements[1][0] = -Math::sin(p_rot) * p_scale.y; - elements[0][1] = Math::sin(p_rot) * p_scale.x; -} - -Rect2 Transform2D::xform_inv(const Rect2 &p_rect) const { - - Vector2 ends[4] = { - xform_inv(p_rect.position), - xform_inv(Vector2(p_rect.position.x, p_rect.position.y + p_rect.size.y)), - xform_inv(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y + p_rect.size.y)), - xform_inv(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y)) - }; - - Rect2 new_rect; - new_rect.position = ends[0]; - new_rect.expand_to(ends[1]); - new_rect.expand_to(ends[2]); - new_rect.expand_to(ends[3]); - - return new_rect; -} - -#endif diff --git a/core/math/math_defs.h b/core/math/math_defs.h index d3484d8d02..db9055cee2 100644 --- a/core/math/math_defs.h +++ b/core/math/math_defs.h @@ -36,30 +36,71 @@ #define CMP_NORMALIZE_TOLERANCE 0.000001 #define CMP_POINT_IN_PLANE_EPSILON 0.00001 +#define Math_SQRT12 0.7071067811865475244008443621048490 +#define Math_SQRT2 1.4142135623730950488016887242 +#define Math_LN2 0.6931471805599453094172321215 +#define Math_TAU 6.2831853071795864769252867666 +#define Math_PI 3.1415926535897932384626433833 +#define Math_E 2.7182818284590452353602874714 +#define Math_INF INFINITY +#define Math_NAN NAN + #ifdef DEBUG_ENABLED #define MATH_CHECKS #endif #define USEC_TO_SEC(m_usec) ((m_usec) / 1000000.0) -/** - * "Real" is a type that will be translated to either floats or fixed depending - * on the compilation setting - */ enum ClockDirection { - CLOCKWISE, COUNTERCLOCKWISE }; -#ifdef REAL_T_IS_DOUBLE +enum Orientation { -typedef double real_t; + HORIZONTAL, + VERTICAL +}; -#else +enum HAlign { -typedef float real_t; + HALIGN_LEFT, + HALIGN_CENTER, + HALIGN_RIGHT +}; + +enum VAlign { + + VALIGN_TOP, + VALIGN_CENTER, + VALIGN_BOTTOM +}; +enum Margin { + + MARGIN_LEFT, + MARGIN_TOP, + MARGIN_RIGHT, + MARGIN_BOTTOM +}; + +enum Corner { + + CORNER_TOP_LEFT, + CORNER_TOP_RIGHT, + CORNER_BOTTOM_RIGHT, + CORNER_BOTTOM_LEFT +}; + +/** + * The "Real" type is an abstract type used for real numbers, such as 1.5, + * in contrast to integer numbers. Precision can be controlled with the + * presence or absence of the REAL_T_IS_DOUBLE define. + */ +#ifdef REAL_T_IS_DOUBLE +typedef double real_t; +#else +typedef float real_t; #endif #endif // MATH_DEFS_H diff --git a/core/helper/math_fieldwise.cpp b/core/math/math_fieldwise.cpp index ff3f8b3520..20b2341ab0 100644 --- a/core/helper/math_fieldwise.cpp +++ b/core/math/math_fieldwise.cpp @@ -30,7 +30,7 @@ #ifdef TOOLS_ENABLED -#include "core/helper/math_fieldwise.h" +#include "math_fieldwise.h" #define SETUP_TYPE(m_type) \ m_type source = p_source; \ diff --git a/core/helper/math_fieldwise.h b/core/math/math_fieldwise.h index 0e7cc3ea4a..0e7cc3ea4a 100644 --- a/core/helper/math_fieldwise.h +++ b/core/math/math_fieldwise.h diff --git a/core/math/math_funcs.cpp b/core/math/math_funcs.cpp index 5c8512d8bd..06355d15ed 100644 --- a/core/math/math_funcs.cpp +++ b/core/math/math_funcs.cpp @@ -30,34 +30,31 @@ #include "math_funcs.h" -#include "core/os/os.h" - -pcg32_random_t Math::default_pcg = { 12047754176567800795ULL, PCG_DEFAULT_INC_64 }; +RandomPCG Math::default_rand(RandomPCG::DEFAULT_SEED, RandomPCG::DEFAULT_INC); #define PHI 0x9e3779b9 -// TODO: we should eventually expose pcg.inc too uint32_t Math::rand_from_seed(uint64_t *seed) { - pcg32_random_t pcg = { *seed, PCG_DEFAULT_INC_64 }; - uint32_t r = pcg32_random_r(&pcg); - *seed = pcg.state; + RandomPCG rng = RandomPCG(*seed, RandomPCG::DEFAULT_INC); + uint32_t r = rng.rand(); + *seed = rng.get_seed(); return r; } void Math::seed(uint64_t x) { - default_pcg.state = x; + default_rand.seed(x); } void Math::randomize() { - seed(OS::get_singleton()->get_ticks_usec() * default_pcg.state + PCG_DEFAULT_INC_64); + default_rand.randomize(); } uint32_t Math::rand() { - return pcg32_random_r(&default_pcg); + return default_rand.rand(); } int Math::step_decimals(double p_step) { - static const int maxn = 9; + static const int maxn = 10; static const double sd[maxn] = { 0.9999, // somehow compensate for floating point error 0.09999, @@ -67,17 +64,19 @@ int Math::step_decimals(double p_step) { 0.000009999, 0.0000009999, 0.00000009999, - 0.000000009999 + 0.000000009999, + 0.0000000009999 }; - double as = Math::abs(p_step); + double abs = Math::abs(p_step); + double decs = abs - (int)abs; // Strip away integer part for (int i = 0; i < maxn; i++) { - if (as >= sd[i]) { + if (decs >= sd[i]) { return i; } } - return maxn; + return 0; } double Math::dectime(double p_value, double p_amount, double p_step) { @@ -167,13 +166,9 @@ uint32_t Math::larger_prime(uint32_t p_val) { } double Math::random(double from, double to) { - unsigned int r = Math::rand(); - double ret = (double)r / (double)RANDOM_MAX; - return (ret) * (to - from) + from; + return default_rand.random(from, to); } float Math::random(float from, float to) { - unsigned int r = Math::rand(); - float ret = (float)r / (float)RANDOM_MAX; - return (ret) * (to - from) + from; + return default_rand.random(from, to); } diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index f0c0268f31..f9d89d5d5a 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -31,29 +31,23 @@ #ifndef MATH_FUNCS_H #define MATH_FUNCS_H -#include "math_defs.h" -#include "typedefs.h" +#include "core/math/math_defs.h" +#include "core/math/random_pcg.h" +#include "core/typedefs.h" #include "thirdparty/misc/pcg.h" #include <float.h> #include <math.h> -#define Math_PI 3.14159265358979323846 -#define Math_TAU 6.28318530717958647692 -#define Math_SQRT12 0.7071067811865475244008443621048490 -#define Math_LN2 0.693147180559945309417 -#define Math_INF INFINITY -#define Math_NAN NAN - class Math { - static pcg32_random_t default_pcg; + static RandomPCG default_rand; public: Math() {} // useless to instance - static const uint64_t RANDOM_MAX = 4294967295; + static const uint64_t RANDOM_MAX = 0xFFFFFFFF; static _ALWAYS_INLINE_ double sin(double p_x) { return ::sin(p_x); } static _ALWAYS_INLINE_ float sin(float p_x) { return ::sinf(p_x); } @@ -312,16 +306,6 @@ public: return b; } -#if defined(__GNUC__) - - static _ALWAYS_INLINE_ int64_t dtoll(double p_double) { return (int64_t)p_double; } ///@TODO OPTIMIZE - static _ALWAYS_INLINE_ int64_t dtoll(float p_float) { return (int64_t)p_float; } ///@TODO OPTIMIZE and rename -#else - - static _ALWAYS_INLINE_ int64_t dtoll(double p_double) { return (int64_t)p_double; } ///@TODO OPTIMIZE - static _ALWAYS_INLINE_ int64_t dtoll(float p_float) { return (int64_t)p_float; } ///@TODO OPTIMIZE and rename -#endif - static _ALWAYS_INLINE_ uint32_t halfbits_to_floatbits(uint16_t h) { uint16_t h_exp, h_sig; uint32_t f_sgn, f_exp, f_sig; diff --git a/core/math/matrix3.cpp b/core/math/matrix3.cpp index 7db41756ed..925a7b3f1e 100644 --- a/core/math/matrix3.cpp +++ b/core/math/matrix3.cpp @@ -29,9 +29,11 @@ /*************************************************************************/ #include "matrix3.h" -#include "math_funcs.h" -#include "os/copymem.h" -#include "print_string.h" + +#include "core/math/math_funcs.h" +#include "core/os/copymem.h" +#include "core/print_string.h" + #define cofac(row1, col1, row2, col2) \ (elements[row1][col1] * elements[row2][col2] - elements[row1][col2] * elements[row2][col1]) @@ -297,14 +299,14 @@ Vector3 Basis::rotref_posscale_decomposition(Basis &rotref) const { ERR_FAIL_COND_V(determinant() == 0, Vector3()); Basis m = transposed() * (*this); - ERR_FAIL_COND_V(m.is_diagonal() == false, Vector3()); + ERR_FAIL_COND_V(!m.is_diagonal(), Vector3()); #endif Vector3 scale = get_scale(); Basis inv_scale = Basis().scaled(scale.inverse()); // this will also absorb the sign of scale rotref = (*this) * inv_scale; #ifdef MATH_CHECKS - ERR_FAIL_COND_V(rotref.is_orthogonal() == false, Vector3()); + ERR_FAIL_COND_V(!rotref.is_orthogonal(), Vector3()); #endif return scale.abs(); } @@ -428,7 +430,7 @@ Vector3 Basis::get_euler_xyz() const { Vector3 euler; #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_rotation() == false, euler); + ERR_FAIL_COND_V(!is_rotation(), euler); #endif real_t sy = elements[0][2]; if (sy < 1.0) { @@ -495,7 +497,7 @@ Vector3 Basis::get_euler_yxz() const { Vector3 euler; #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_rotation() == false, euler); + ERR_FAIL_COND_V(!is_rotation(), euler); #endif real_t m12 = elements[1][2]; @@ -554,7 +556,7 @@ bool Basis::is_equal_approx(const Basis &a, const Basis &b) const { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - if (Math::is_equal_approx(a.elements[i][j], b.elements[i][j]) == false) + if (!Math::is_equal_approx(a.elements[i][j], b.elements[i][j])) return false; } } @@ -598,7 +600,7 @@ Basis::operator String() const { Quat Basis::get_quat() const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_rotation() == false, Quat()); + ERR_FAIL_COND_V(!is_rotation(), Quat()); #endif real_t trace = elements[0][0] + elements[1][1] + elements[2][2]; real_t temp[4]; @@ -695,7 +697,7 @@ void Basis::set_orthogonal_index(int p_index) { void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const { #ifdef MATH_CHECKS - ERR_FAIL_COND(is_rotation() == false); + ERR_FAIL_COND(!is_rotation()); #endif real_t angle, x, y, z; // variables for result real_t epsilon = 0.01; // margin to allow for rounding errors @@ -783,7 +785,7 @@ void Basis::set_quat(const Quat &p_quat) { void Basis::set_axis_angle(const Vector3 &p_axis, real_t p_phi) { // Rotation matrix from axis and angle, see https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_angle #ifdef MATH_CHECKS - ERR_FAIL_COND(p_axis.is_normalized() == false); + ERR_FAIL_COND(!p_axis.is_normalized()); #endif Vector3 axis_sq(p_axis.x * p_axis.x, p_axis.y * p_axis.y, p_axis.z * p_axis.z); @@ -835,8 +837,8 @@ void Basis::set_diagonal(const Vector3 p_diag) { Basis Basis::slerp(const Basis &target, const real_t &t) const { // TODO: implement this directly without using quaternions to make it more efficient #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_rotation() == false, Basis()); - ERR_FAIL_COND_V(target.is_rotation() == false, Basis()); + ERR_FAIL_COND_V(!is_rotation(), Basis()); + ERR_FAIL_COND_V(!target.is_rotation(), Basis()); #endif Quat from(*this); diff --git a/core/math/matrix3.h b/core/math/matrix3.h index 9ff1a97dc9..35bf75bbe4 100644 --- a/core/math/matrix3.h +++ b/core/math/matrix3.h @@ -28,16 +28,18 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "vector3.h" +// Circular dependency between Vector3 and Basis :/ +#include "core/math/vector3.h" #ifndef MATRIX3_H #define MATRIX3_H -#include "quat.h" +#include "core/math/quat.h" /** @author Juan Linietsky <reduzio@gmail.com> */ + class Basis { public: Vector3 elements[3]; diff --git a/core/math/octree.h b/core/math/octree.h index 4e3d6257f0..cd89743a5a 100644 --- a/core/math/octree.h +++ b/core/math/octree.h @@ -31,12 +31,12 @@ #ifndef OCTREE_H #define OCTREE_H -#include "aabb.h" -#include "list.h" -#include "map.h" -#include "print_string.h" -#include "variant.h" -#include "vector3.h" +#include "core/list.h" +#include "core/map.h" +#include "core/math/aabb.h" +#include "core/math/vector3.h" +#include "core/print_string.h" +#include "core/variant.h" /** @author Juan Linietsky <reduzio@gmail.com> @@ -478,7 +478,7 @@ void Octree<T, use_pairs, AL>::_insert_element(Element *p_element, Octant *p_oct splits++; } } else { - /* check againt AABB where child should be */ + /* check against AABB where child should be */ AABB aabb = p_octant->aabb; aabb.size *= 0.5; diff --git a/core/math/plane.cpp b/core/math/plane.cpp index 78bb1771a4..3c597d57f8 100644 --- a/core/math/plane.cpp +++ b/core/math/plane.cpp @@ -30,7 +30,7 @@ #include "plane.h" -#include "math_funcs.h" +#include "core/math/math_funcs.h" #define _PLANE_EQ_DOT_EPSILON 0.999 #define _PLANE_EQ_D_EPSILON 0.0001 diff --git a/core/math/plane.h b/core/math/plane.h index e567422dd0..4eedebb79e 100644 --- a/core/math/plane.h +++ b/core/math/plane.h @@ -31,7 +31,7 @@ #ifndef PLANE_H #define PLANE_H -#include "vector3.h" +#include "core/math/vector3.h" class Plane { public: diff --git a/core/math/quat.cpp b/core/math/quat.cpp index 67c9048a41..791e84f089 100644 --- a/core/math/quat.cpp +++ b/core/math/quat.cpp @@ -29,8 +29,9 @@ /*************************************************************************/ #include "quat.h" -#include "matrix3.h" -#include "print_string.h" + +#include "core/math/matrix3.h" +#include "core/print_string.h" // set_euler_xyz expects a vector containing the Euler angles in the format // (ax,ay,az), where ax is the angle of rotation around x axis, @@ -99,7 +100,7 @@ void Quat::set_euler_yxz(const Vector3 &p_euler) { // This implementation uses YXZ convention (Z is the first rotation). Vector3 Quat::get_euler_yxz() const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_normalized() == false, Vector3(0, 0, 0)); + ERR_FAIL_COND_V(!is_normalized(), Vector3(0, 0, 0)); #endif Basis m(*this); return m.get_euler_yxz(); @@ -134,20 +135,20 @@ Quat Quat::normalized() const { } bool Quat::is_normalized() const { - return Math::is_equal_approx(length(), 1.0); + return Math::is_equal_approx(length_squared(), 1.0); } Quat Quat::inverse() const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_normalized() == false, Quat()); + ERR_FAIL_COND_V(!is_normalized(), Quat()); #endif return Quat(-x, -y, -z, w); } Quat Quat::slerp(const Quat &q, const real_t &t) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_normalized() == false, Quat()); - ERR_FAIL_COND_V(q.is_normalized() == false, Quat()); + ERR_FAIL_COND_V(!is_normalized(), Quat()); + ERR_FAIL_COND_V(!q.is_normalized(), Quat()); #endif Quat to1; real_t omega, cosom, sinom, scale0, scale1; @@ -193,8 +194,8 @@ Quat Quat::slerp(const Quat &q, const real_t &t) const { Quat Quat::slerpni(const Quat &q, const real_t &t) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_normalized() == false, Quat()); - ERR_FAIL_COND_V(q.is_normalized() == false, Quat()); + ERR_FAIL_COND_V(!is_normalized(), Quat()); + ERR_FAIL_COND_V(!q.is_normalized(), Quat()); #endif const Quat &from = *this; @@ -215,8 +216,8 @@ Quat Quat::slerpni(const Quat &q, const real_t &t) const { Quat Quat::cubic_slerp(const Quat &q, const Quat &prep, const Quat &postq, const real_t &t) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_normalized() == false, Quat()); - ERR_FAIL_COND_V(q.is_normalized() == false, Quat()); + ERR_FAIL_COND_V(!is_normalized(), Quat()); + ERR_FAIL_COND_V(!q.is_normalized(), Quat()); #endif //the only way to do slerp :| real_t t2 = (1.0 - t) * t * 2; @@ -232,7 +233,7 @@ Quat::operator String() const { void Quat::set_axis_angle(const Vector3 &axis, const real_t &angle) { #ifdef MATH_CHECKS - ERR_FAIL_COND(axis.is_normalized() == false); + ERR_FAIL_COND(!axis.is_normalized()); #endif real_t d = axis.length(); if (d == 0) diff --git a/core/math/quat.h b/core/math/quat.h index 6dc8d66f60..c4f9b3a732 100644 --- a/core/math/quat.h +++ b/core/math/quat.h @@ -28,18 +28,20 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "vector3.h" +// Circular dependency between Vector3 and Basis :/ +#include "core/math/vector3.h" #ifndef QUAT_H #define QUAT_H -#include "math_defs.h" -#include "math_funcs.h" -#include "ustring.h" +#include "core/math/math_defs.h" +#include "core/math/math_funcs.h" +#include "core/ustring.h" /** @author Juan Linietsky <reduzio@gmail.com> */ + class Quat { public: real_t x, y, z, w; @@ -85,7 +87,7 @@ public: _FORCE_INLINE_ Vector3 xform(const Vector3 &v) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_normalized() == false, v); + ERR_FAIL_COND_V(!is_normalized(), v); #endif Vector3 u(x, y, z); Vector3 uv = u.cross(v); diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp index cb923d264e..23823b339a 100644 --- a/core/math/quick_hull.cpp +++ b/core/math/quick_hull.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "quick_hull.h" -#include "map.h" + +#include "core/map.h" uint32_t QuickHull::debug_stop_after = 0xFFFFFFFF; @@ -62,7 +63,6 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me Vector3 sp = p_points[i].snapped(Vector3(0.0001, 0.0001, 0.0001)); if (valid_cache.has(sp)) { valid_points.write[i] = false; - //print_line("INVALIDATED: "+itos(i)); } else { valid_points.write[i] = true; valid_cache.insert(sp); @@ -397,7 +397,6 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me Map<Edge, RetFaceConnect>::Element *F = ret_edges.find(e); ERR_CONTINUE(!F); - List<Geometry::MeshData::Face>::Element *O = F->get().left == E ? F->get().right : F->get().left; ERR_CONTINUE(O == E); ERR_CONTINUE(O == NULL); @@ -426,7 +425,6 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me Edge e2(idx, idxn); Map<Edge, RetFaceConnect>::Element *F2 = ret_edges.find(e2); - ERR_CONTINUE(!F2); //change faceconnect, point to this face instead if (F2->get().left == O) @@ -439,6 +437,15 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me } } + // remove all edge connections to this face + for (Map<Edge, RetFaceConnect>::Element *E = ret_edges.front(); E; E = E->next()) { + if (E->get().left == O) + E->get().left = NULL; + + if (E->get().right == O) + E->get().right = NULL; + } + ret_edges.erase(F); //remove the edge ret_faces.erase(O); //remove the face } @@ -448,7 +455,6 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me //fill mesh r_mesh.faces.clear(); r_mesh.faces.resize(ret_faces.size()); - //print_line("FACECOUNT: "+itos(r_mesh.faces.size())); int idx = 0; for (List<Geometry::MeshData::Face>::Element *E = ret_faces.front(); E; E = E->next()) { @@ -466,12 +472,5 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me r_mesh.vertices = p_points; - //r_mesh.optimize_vertices(); - /* - print_line("FACES: "+itos(r_mesh.faces.size())); - print_line("EDGES: "+itos(r_mesh.edges.size())); - print_line("VERTICES: "+itos(r_mesh.vertices.size())); -*/ - return OK; } diff --git a/core/math/quick_hull.h b/core/math/quick_hull.h index eef4a9adff..0ac2758323 100644 --- a/core/math/quick_hull.h +++ b/core/math/quick_hull.h @@ -31,10 +31,10 @@ #ifndef QUICK_HULL_H #define QUICK_HULL_H -#include "aabb.h" -#include "geometry.h" -#include "list.h" -#include "set.h" +#include "core/list.h" +#include "core/math/aabb.h" +#include "core/math/geometry.h" +#include "core/set.h" class QuickHull { diff --git a/core/math/random_number_generator.cpp b/core/math/random_number_generator.cpp new file mode 100644 index 0000000000..e4ec0dac99 --- /dev/null +++ b/core/math/random_number_generator.cpp @@ -0,0 +1,45 @@ +/*************************************************************************/ +/* random_number_generator.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "random_number_generator.h" + +RandomNumberGenerator::RandomNumberGenerator() : + randbase() {} + +void RandomNumberGenerator::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_seed", "seed"), &RandomNumberGenerator::set_seed); + ClassDB::bind_method(D_METHOD("get_seed"), &RandomNumberGenerator::get_seed); + ADD_PROPERTY(PropertyInfo(Variant::INT, "seed"), "set_seed", "get_seed"); + + ClassDB::bind_method(D_METHOD("randi"), &RandomNumberGenerator::randi); + ClassDB::bind_method(D_METHOD("randf"), &RandomNumberGenerator::randf); + ClassDB::bind_method(D_METHOD("rand_range", "from", "to"), &RandomNumberGenerator::rand_range); + ClassDB::bind_method(D_METHOD("randomize"), &RandomNumberGenerator::randomize); +} diff --git a/core/math/random_number_generator.h b/core/math/random_number_generator.h new file mode 100644 index 0000000000..557863fdbd --- /dev/null +++ b/core/math/random_number_generator.h @@ -0,0 +1,61 @@ +/*************************************************************************/ +/* random_number_generator.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RANDOM_NUMBER_GENERATOR_H +#define RANDOM_NUMBER_GENERATOR_H + +#include "core/math/random_pcg.h" +#include "core/reference.h" + +class RandomNumberGenerator : public Reference { + GDCLASS(RandomNumberGenerator, Reference); + + RandomPCG randbase; + +protected: + static void _bind_methods(); + +public: + _FORCE_INLINE_ void set_seed(uint64_t seed) { randbase.seed(seed); } + + _FORCE_INLINE_ uint64_t get_seed() { return randbase.get_seed(); } + + _FORCE_INLINE_ void randomize() { return randbase.randomize(); } + + _FORCE_INLINE_ uint32_t randi() { return randbase.rand(); } + + _FORCE_INLINE_ real_t randf() { return randbase.randf(); } + + _FORCE_INLINE_ real_t rand_range(real_t from, real_t to) { return randbase.random(from, to); } + + RandomNumberGenerator(); +}; + +#endif // RANDOM_NUMBER_GENERATOR_H diff --git a/core/math/random_pcg.cpp b/core/math/random_pcg.cpp new file mode 100644 index 0000000000..16899f79da --- /dev/null +++ b/core/math/random_pcg.cpp @@ -0,0 +1,55 @@ +/*************************************************************************/ +/* random_pcg.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "random_pcg.h" + +#include "core/os/os.h" + +RandomPCG::RandomPCG(uint64_t seed, uint64_t inc) : + pcg() { + pcg.state = seed; + pcg.inc = inc; +} + +void RandomPCG::randomize() { + seed(OS::get_singleton()->get_ticks_usec() * pcg.state + PCG_DEFAULT_INC_64); +} + +double RandomPCG::random(double from, double to) { + unsigned int r = rand(); + double ret = (double)r / (double)RANDOM_MAX; + return (ret) * (to - from) + from; +} + +float RandomPCG::random(float from, float to) { + unsigned int r = rand(); + float ret = (float)r / (float)RANDOM_MAX; + return (ret) * (to - from) + from; +} diff --git a/core/math/random_pcg.h b/core/math/random_pcg.h new file mode 100644 index 0000000000..4a43c36ede --- /dev/null +++ b/core/math/random_pcg.h @@ -0,0 +1,61 @@ +/*************************************************************************/ +/* random_pcg.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RANDOM_PCG_H +#define RANDOM_PCG_H + +#include "core/math/math_defs.h" + +#include "thirdparty/misc/pcg.h" + +class RandomPCG { + pcg32_random_t pcg; + +public: + static const uint64_t DEFAULT_SEED = 12047754176567800795ULL; + static const uint64_t DEFAULT_INC = PCG_DEFAULT_INC_64; + static const uint64_t RANDOM_MAX = 4294967295; + + RandomPCG(uint64_t seed = DEFAULT_SEED, uint64_t inc = PCG_DEFAULT_INC_64); + + _FORCE_INLINE_ void seed(uint64_t seed) { pcg.state = seed; } + _FORCE_INLINE_ uint64_t get_seed() { return pcg.state; } + + void randomize(); + _FORCE_INLINE_ uint32_t rand() { return pcg32_random_r(&pcg); } + _FORCE_INLINE_ double randf() { return (double)rand() / (double)RANDOM_MAX; } + _FORCE_INLINE_ float randd() { return (float)rand() / (float)RANDOM_MAX; } + + double random(double from, double to); + float random(float from, float to); + real_t random(int from, int to) { return (real_t)random((real_t)from, (real_t)to); } +}; + +#endif // RANDOM_PCG_H diff --git a/core/math/rect2.cpp b/core/math/rect2.cpp new file mode 100644 index 0000000000..24c1c8c984 --- /dev/null +++ b/core/math/rect2.cpp @@ -0,0 +1,240 @@ +/*************************************************************************/ +/* rect2.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "core/math/transform_2d.h" // Includes rect2.h but Rect2 needs Transform2D + +bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos, Point2 *r_normal) const { + + real_t min = 0, max = 1; + int axis = 0; + real_t sign = 0; + + for (int i = 0; i < 2; i++) { + real_t seg_from = p_from[i]; + real_t seg_to = p_to[i]; + real_t box_begin = position[i]; + real_t box_end = box_begin + size[i]; + real_t cmin, cmax; + real_t csign; + + if (seg_from < seg_to) { + + if (seg_from > box_end || seg_to < box_begin) + return false; + real_t length = seg_to - seg_from; + cmin = (seg_from < box_begin) ? ((box_begin - seg_from) / length) : 0; + cmax = (seg_to > box_end) ? ((box_end - seg_from) / length) : 1; + csign = -1.0; + + } else { + + if (seg_to > box_end || seg_from < box_begin) + return false; + real_t length = seg_to - seg_from; + cmin = (seg_from > box_end) ? (box_end - seg_from) / length : 0; + cmax = (seg_to < box_begin) ? (box_begin - seg_from) / length : 1; + csign = 1.0; + } + + if (cmin > min) { + min = cmin; + axis = i; + sign = csign; + } + if (cmax < max) + max = cmax; + if (max < min) + return false; + } + + Vector2 rel = p_to - p_from; + + if (r_normal) { + Vector2 normal; + normal[axis] = sign; + *r_normal = normal; + } + + if (r_pos) + *r_pos = p_from + rel * min; + + return true; +} + +bool Rect2::intersects_transformed(const Transform2D &p_xform, const Rect2 &p_rect) const { + + //SAT intersection between local and transformed rect2 + + Vector2 xf_points[4] = { + p_xform.xform(p_rect.position), + p_xform.xform(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y)), + p_xform.xform(Vector2(p_rect.position.x, p_rect.position.y + p_rect.size.y)), + p_xform.xform(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y + p_rect.size.y)), + }; + + real_t low_limit; + + //base rect2 first (faster) + + if (xf_points[0].y > position.y) + goto next1; + if (xf_points[1].y > position.y) + goto next1; + if (xf_points[2].y > position.y) + goto next1; + if (xf_points[3].y > position.y) + goto next1; + + return false; + +next1: + + low_limit = position.y + size.y; + + if (xf_points[0].y < low_limit) + goto next2; + if (xf_points[1].y < low_limit) + goto next2; + if (xf_points[2].y < low_limit) + goto next2; + if (xf_points[3].y < low_limit) + goto next2; + + return false; + +next2: + + if (xf_points[0].x > position.x) + goto next3; + if (xf_points[1].x > position.x) + goto next3; + if (xf_points[2].x > position.x) + goto next3; + if (xf_points[3].x > position.x) + goto next3; + + return false; + +next3: + + low_limit = position.x + size.x; + + if (xf_points[0].x < low_limit) + goto next4; + if (xf_points[1].x < low_limit) + goto next4; + if (xf_points[2].x < low_limit) + goto next4; + if (xf_points[3].x < low_limit) + goto next4; + + return false; + +next4: + + Vector2 xf_points2[4] = { + position, + Vector2(position.x + size.x, position.y), + Vector2(position.x, position.y + size.y), + Vector2(position.x + size.x, position.y + size.y), + }; + + real_t maxa = p_xform.elements[0].dot(xf_points2[0]); + real_t mina = maxa; + + real_t dp = p_xform.elements[0].dot(xf_points2[1]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + dp = p_xform.elements[0].dot(xf_points2[2]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + dp = p_xform.elements[0].dot(xf_points2[3]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + real_t maxb = p_xform.elements[0].dot(xf_points[0]); + real_t minb = maxb; + + dp = p_xform.elements[0].dot(xf_points[1]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + dp = p_xform.elements[0].dot(xf_points[2]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + dp = p_xform.elements[0].dot(xf_points[3]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + if (mina > maxb) + return false; + if (minb > maxa) + return false; + + maxa = p_xform.elements[1].dot(xf_points2[0]); + mina = maxa; + + dp = p_xform.elements[1].dot(xf_points2[1]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + dp = p_xform.elements[1].dot(xf_points2[2]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + dp = p_xform.elements[1].dot(xf_points2[3]); + maxa = MAX(dp, maxa); + mina = MIN(dp, mina); + + maxb = p_xform.elements[1].dot(xf_points[0]); + minb = maxb; + + dp = p_xform.elements[1].dot(xf_points[1]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + dp = p_xform.elements[1].dot(xf_points[2]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + dp = p_xform.elements[1].dot(xf_points[3]); + maxb = MAX(dp, maxb); + minb = MIN(dp, minb); + + if (mina > maxb) + return false; + if (minb > maxa) + return false; + + return true; +} diff --git a/core/math/rect2.h b/core/math/rect2.h new file mode 100644 index 0000000000..96c0e177d3 --- /dev/null +++ b/core/math/rect2.h @@ -0,0 +1,371 @@ +/*************************************************************************/ +/* rect2.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RECT2_H +#define RECT2_H + +#include "core/math/vector2.h" // also includes math_funcs and ustring + +struct Transform2D; + +struct Rect2 { + + Point2 position; + Size2 size; + + const Vector2 &get_position() const { return position; } + void set_position(const Vector2 &p_pos) { position = p_pos; } + const Vector2 &get_size() const { return size; } + void set_size(const Vector2 &p_size) { size = p_size; } + + real_t get_area() const { return size.width * size.height; } + + inline bool intersects(const Rect2 &p_rect) const { + if (position.x >= (p_rect.position.x + p_rect.size.width)) + return false; + if ((position.x + size.width) <= p_rect.position.x) + return false; + if (position.y >= (p_rect.position.y + p_rect.size.height)) + return false; + if ((position.y + size.height) <= p_rect.position.y) + return false; + + return true; + } + + inline real_t distance_to(const Vector2 &p_point) const { + + real_t dist = 0.0; + bool inside = true; + + if (p_point.x < position.x) { + real_t d = position.x - p_point.x; + dist = inside ? d : MIN(dist, d); + inside = false; + } + if (p_point.y < position.y) { + real_t d = position.y - p_point.y; + dist = inside ? d : MIN(dist, d); + inside = false; + } + if (p_point.x >= (position.x + size.x)) { + real_t d = p_point.x - (position.x + size.x); + dist = inside ? d : MIN(dist, d); + inside = false; + } + if (p_point.y >= (position.y + size.y)) { + real_t d = p_point.y - (position.y + size.y); + dist = inside ? d : MIN(dist, d); + inside = false; + } + + if (inside) + return 0; + else + return dist; + } + + bool intersects_transformed(const Transform2D &p_xform, const Rect2 &p_rect) const; + + bool intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos = NULL, Point2 *r_normal = NULL) const; + + inline bool encloses(const Rect2 &p_rect) const { + + return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && + ((p_rect.position.x + p_rect.size.x) < (position.x + size.x)) && + ((p_rect.position.y + p_rect.size.y) < (position.y + size.y)); + } + + inline bool has_no_area() const { + + return (size.x <= 0 || size.y <= 0); + } + inline Rect2 clip(const Rect2 &p_rect) const { /// return a clipped rect + + Rect2 new_rect = p_rect; + + if (!intersects(new_rect)) + return Rect2(); + + new_rect.position.x = MAX(p_rect.position.x, position.x); + new_rect.position.y = MAX(p_rect.position.y, position.y); + + Point2 p_rect_end = p_rect.position + p_rect.size; + Point2 end = position + size; + + new_rect.size.x = MIN(p_rect_end.x, end.x) - new_rect.position.x; + new_rect.size.y = MIN(p_rect_end.y, end.y) - new_rect.position.y; + + return new_rect; + } + + inline Rect2 merge(const Rect2 &p_rect) const { ///< return a merged rect + + Rect2 new_rect; + + new_rect.position.x = MIN(p_rect.position.x, position.x); + new_rect.position.y = MIN(p_rect.position.y, position.y); + + new_rect.size.x = MAX(p_rect.position.x + p_rect.size.x, position.x + size.x); + new_rect.size.y = MAX(p_rect.position.y + p_rect.size.y, position.y + size.y); + + new_rect.size = new_rect.size - new_rect.position; //make relative again + + return new_rect; + }; + inline bool has_point(const Point2 &p_point) const { + if (p_point.x < position.x) + return false; + if (p_point.y < position.y) + return false; + + if (p_point.x >= (position.x + size.x)) + return false; + if (p_point.y >= (position.y + size.y)) + return false; + + return true; + } + + inline bool no_area() const { return (size.width <= 0 || size.height <= 0); } + + bool operator==(const Rect2 &p_rect) const { return position == p_rect.position && size == p_rect.size; } + bool operator!=(const Rect2 &p_rect) const { return position != p_rect.position || size != p_rect.size; } + + inline Rect2 grow(real_t p_by) const { + + Rect2 g = *this; + g.position.x -= p_by; + g.position.y -= p_by; + g.size.width += p_by * 2; + g.size.height += p_by * 2; + return g; + } + + inline Rect2 grow_margin(Margin p_margin, real_t p_amount) const { + Rect2 g = *this; + g = g.grow_individual((MARGIN_LEFT == p_margin) ? p_amount : 0, + (MARGIN_TOP == p_margin) ? p_amount : 0, + (MARGIN_RIGHT == p_margin) ? p_amount : 0, + (MARGIN_BOTTOM == p_margin) ? p_amount : 0); + return g; + } + + inline Rect2 grow_individual(real_t p_left, real_t p_top, real_t p_right, real_t p_bottom) const { + + Rect2 g = *this; + g.position.x -= p_left; + g.position.y -= p_top; + g.size.width += p_left + p_right; + g.size.height += p_top + p_bottom; + + return g; + } + + inline Rect2 expand(const Vector2 &p_vector) const { + + Rect2 r = *this; + r.expand_to(p_vector); + return r; + } + + inline void expand_to(const Vector2 &p_vector) { //in place function for speed + + Vector2 begin = position; + Vector2 end = position + size; + + if (p_vector.x < begin.x) + begin.x = p_vector.x; + if (p_vector.y < begin.y) + begin.y = p_vector.y; + + if (p_vector.x > end.x) + end.x = p_vector.x; + if (p_vector.y > end.y) + end.y = p_vector.y; + + position = begin; + size = end - begin; + } + + inline Rect2 abs() const { + + return Rect2(Point2(position.x + MIN(size.x, 0), position.y + MIN(size.y, 0)), size.abs()); + } + + operator String() const { return String(position) + ", " + String(size); } + + Rect2() {} + Rect2(real_t p_x, real_t p_y, real_t p_width, real_t p_height) : + position(Point2(p_x, p_y)), + size(Size2(p_width, p_height)) { + } + Rect2(const Point2 &p_pos, const Size2 &p_size) : + position(p_pos), + size(p_size) { + } +}; + +struct Rect2i { + + Point2i position; + Size2i size; + + const Point2i &get_position() const { return position; } + void set_position(const Point2i &p_position) { position = p_position; } + const Size2i &get_size() const { return size; } + void set_size(const Size2i &p_size) { size = p_size; } + + int get_area() const { return size.width * size.height; } + + inline bool intersects(const Rect2i &p_rect) const { + if (position.x > (p_rect.position.x + p_rect.size.width)) + return false; + if ((position.x + size.width) < p_rect.position.x) + return false; + if (position.y > (p_rect.position.y + p_rect.size.height)) + return false; + if ((position.y + size.height) < p_rect.position.y) + return false; + + return true; + } + + inline bool encloses(const Rect2i &p_rect) const { + + return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) && + ((p_rect.position.x + p_rect.size.x) < (position.x + size.x)) && + ((p_rect.position.y + p_rect.size.y) < (position.y + size.y)); + } + + inline bool has_no_area() const { + + return (size.x <= 0 || size.y <= 0); + } + inline Rect2i clip(const Rect2i &p_rect) const { /// return a clipped rect + + Rect2i new_rect = p_rect; + + if (!intersects(new_rect)) + return Rect2i(); + + new_rect.position.x = MAX(p_rect.position.x, position.x); + new_rect.position.y = MAX(p_rect.position.y, position.y); + + Point2 p_rect_end = p_rect.position + p_rect.size; + Point2 end = position + size; + + new_rect.size.x = (int)(MIN(p_rect_end.x, end.x) - new_rect.position.x); + new_rect.size.y = (int)(MIN(p_rect_end.y, end.y) - new_rect.position.y); + + return new_rect; + } + + inline Rect2i merge(const Rect2i &p_rect) const { ///< return a merged rect + + Rect2i new_rect; + + new_rect.position.x = MIN(p_rect.position.x, position.x); + new_rect.position.y = MIN(p_rect.position.y, position.y); + + new_rect.size.x = MAX(p_rect.position.x + p_rect.size.x, position.x + size.x); + new_rect.size.y = MAX(p_rect.position.y + p_rect.size.y, position.y + size.y); + + new_rect.size = new_rect.size - new_rect.position; //make relative again + + return new_rect; + }; + bool has_point(const Point2 &p_point) const { + if (p_point.x < position.x) + return false; + if (p_point.y < position.y) + return false; + + if (p_point.x >= (position.x + size.x)) + return false; + if (p_point.y >= (position.y + size.y)) + return false; + + return true; + } + + bool no_area() { return (size.width <= 0 || size.height <= 0); } + + bool operator==(const Rect2i &p_rect) const { return position == p_rect.position && size == p_rect.size; } + bool operator!=(const Rect2i &p_rect) const { return position != p_rect.position || size != p_rect.size; } + + Rect2i grow(int p_by) const { + + Rect2i g = *this; + g.position.x -= p_by; + g.position.y -= p_by; + g.size.width += p_by * 2; + g.size.height += p_by * 2; + return g; + } + + inline void expand_to(const Point2i &p_vector) { + + Point2i begin = position; + Point2i end = position + size; + + if (p_vector.x < begin.x) + begin.x = p_vector.x; + if (p_vector.y < begin.y) + begin.y = p_vector.y; + + if (p_vector.x > end.x) + end.x = p_vector.x; + if (p_vector.y > end.y) + end.y = p_vector.y; + + position = begin; + size = end - begin; + } + + operator String() const { return String(position) + ", " + String(size); } + + operator Rect2() const { return Rect2(position, size); } + Rect2i(const Rect2 &p_r2) : + position(p_r2.position), + size(p_r2.size) { + } + Rect2i() {} + Rect2i(int p_x, int p_y, int p_width, int p_height) : + position(Point2(p_x, p_y)), + size(Size2(p_width, p_height)) { + } + Rect2i(const Point2 &p_pos, const Size2 &p_size) : + position(p_pos), + size(p_size) { + } +}; + +#endif // RECT2_H diff --git a/core/math/transform.cpp b/core/math/transform.cpp index 976e0f174e..75257a6e60 100644 --- a/core/math/transform.cpp +++ b/core/math/transform.cpp @@ -29,9 +29,10 @@ /*************************************************************************/ #include "transform.h" -#include "math_funcs.h" -#include "os/copymem.h" -#include "print_string.h" + +#include "core/math/math_funcs.h" +#include "core/os/copymem.h" +#include "core/print_string.h" void Transform::affine_invert() { diff --git a/core/math/transform.h b/core/math/transform.h index c06eaec604..97c8bf9ab0 100644 --- a/core/math/transform.h +++ b/core/math/transform.h @@ -31,12 +31,14 @@ #ifndef TRANSFORM_H #define TRANSFORM_H -#include "aabb.h" -#include "matrix3.h" -#include "plane.h" +#include "core/math/aabb.h" +#include "core/math/matrix3.h" +#include "core/math/plane.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ + class Transform { public: Basis basis; diff --git a/core/math/math_2d.cpp b/core/math/transform_2d.cpp index a053ffbd93..4bb763c879 100644 --- a/core/math/math_2d.cpp +++ b/core/math/transform_2d.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* math_2d.cpp */ +/* transform_2d.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,287 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "math_2d.h" - -real_t Vector2::angle() const { - - return Math::atan2(y, x); -} - -real_t Vector2::length() const { - - return Math::sqrt(x * x + y * y); -} - -real_t Vector2::length_squared() const { - - return x * x + y * y; -} - -void Vector2::normalize() { - - real_t l = x * x + y * y; - if (l != 0) { - - l = Math::sqrt(l); - x /= l; - y /= l; - } -} - -Vector2 Vector2::normalized() const { - - Vector2 v = *this; - v.normalize(); - return v; -} - -bool Vector2::is_normalized() const { - // use length_squared() instead of length() to avoid sqrt(), makes it more stringent. - return Math::is_equal_approx(length_squared(), 1.0); -} - -real_t Vector2::distance_to(const Vector2 &p_vector2) const { - - return Math::sqrt((x - p_vector2.x) * (x - p_vector2.x) + (y - p_vector2.y) * (y - p_vector2.y)); -} - -real_t Vector2::distance_squared_to(const Vector2 &p_vector2) const { - - return (x - p_vector2.x) * (x - p_vector2.x) + (y - p_vector2.y) * (y - p_vector2.y); -} - -real_t Vector2::angle_to(const Vector2 &p_vector2) const { - - return Math::atan2(cross(p_vector2), dot(p_vector2)); -} - -real_t Vector2::angle_to_point(const Vector2 &p_vector2) const { - - return Math::atan2(y - p_vector2.y, x - p_vector2.x); -} - -real_t Vector2::dot(const Vector2 &p_other) const { - - return x * p_other.x + y * p_other.y; -} - -real_t Vector2::cross(const Vector2 &p_other) const { - - return x * p_other.y - y * p_other.x; -} - -Vector2 Vector2::floor() const { - - return Vector2(Math::floor(x), Math::floor(y)); -} - -Vector2 Vector2::ceil() const { - - return Vector2(Math::ceil(x), Math::ceil(y)); -} - -Vector2 Vector2::round() const { - - return Vector2(Math::round(x), Math::round(y)); -} - -Vector2 Vector2::rotated(real_t p_by) const { - - Vector2 v; - v.set_rotation(angle() + p_by); - v *= length(); - return v; -} - -Vector2 Vector2::project(const Vector2 &p_vec) const { - - Vector2 v1 = p_vec; - Vector2 v2 = *this; - return v2 * (v1.dot(v2) / v2.dot(v2)); -} - -Vector2 Vector2::snapped(const Vector2 &p_by) const { - - return Vector2( - Math::stepify(x, p_by.x), - Math::stepify(y, p_by.y)); -} - -Vector2 Vector2::clamped(real_t p_len) const { - - real_t l = length(); - Vector2 v = *this; - if (l > 0 && p_len < l) { - - v /= l; - v *= p_len; - } - - return v; -} - -Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_t) const { - - Vector2 p0 = p_pre_a; - Vector2 p1 = *this; - Vector2 p2 = p_b; - Vector2 p3 = p_post_b; - - real_t t = p_t; - real_t t2 = t * t; - real_t t3 = t2 * t; - - Vector2 out; - out = 0.5 * ((p1 * 2.0) + - (-p0 + p2) * t + - (2.0 * p0 - 5.0 * p1 + 4 * p2 - p3) * t2 + - (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3); - return out; -} - -// slide returns the component of the vector along the given plane, specified by its normal vector. -Vector2 Vector2::slide(const Vector2 &p_normal) const { -#ifdef MATH_CHECKS - ERR_FAIL_COND_V(p_normal.is_normalized() == false, Vector2()); -#endif - return *this - p_normal * this->dot(p_normal); -} - -Vector2 Vector2::bounce(const Vector2 &p_normal) const { - return -reflect(p_normal); -} - -Vector2 Vector2::reflect(const Vector2 &p_normal) const { -#ifdef MATH_CHECKS - ERR_FAIL_COND_V(p_normal.is_normalized() == false, Vector2()); -#endif - return 2.0 * p_normal * this->dot(p_normal) - *this; -} - -bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos, Point2 *r_normal) const { - - real_t min = 0, max = 1; - int axis = 0; - real_t sign = 0; - - for (int i = 0; i < 2; i++) { - real_t seg_from = p_from[i]; - real_t seg_to = p_to[i]; - real_t box_begin = position[i]; - real_t box_end = box_begin + size[i]; - real_t cmin, cmax; - real_t csign; - - if (seg_from < seg_to) { - - if (seg_from > box_end || seg_to < box_begin) - return false; - real_t length = seg_to - seg_from; - cmin = (seg_from < box_begin) ? ((box_begin - seg_from) / length) : 0; - cmax = (seg_to > box_end) ? ((box_end - seg_from) / length) : 1; - csign = -1.0; - - } else { - - if (seg_to > box_end || seg_from < box_begin) - return false; - real_t length = seg_to - seg_from; - cmin = (seg_from > box_end) ? (box_end - seg_from) / length : 0; - cmax = (seg_to < box_begin) ? (box_begin - seg_from) / length : 1; - csign = 1.0; - } - - if (cmin > min) { - min = cmin; - axis = i; - sign = csign; - } - if (cmax < max) - max = cmax; - if (max < min) - return false; - } - - Vector2 rel = p_to - p_from; - - if (r_normal) { - Vector2 normal; - normal[axis] = sign; - *r_normal = normal; - } - - if (r_pos) - *r_pos = p_from + rel * min; - - return true; -} - -/* Point2i */ - -Point2i Point2i::operator+(const Point2i &p_v) const { - - return Point2i(x + p_v.x, y + p_v.y); -} -void Point2i::operator+=(const Point2i &p_v) { - - x += p_v.x; - y += p_v.y; -} -Point2i Point2i::operator-(const Point2i &p_v) const { - - return Point2i(x - p_v.x, y - p_v.y); -} -void Point2i::operator-=(const Point2i &p_v) { - - x -= p_v.x; - y -= p_v.y; -} - -Point2i Point2i::operator*(const Point2i &p_v1) const { - - return Point2i(x * p_v1.x, y * p_v1.y); -}; - -Point2i Point2i::operator*(const int &rvalue) const { - - return Point2i(x * rvalue, y * rvalue); -}; -void Point2i::operator*=(const int &rvalue) { - - x *= rvalue; - y *= rvalue; -}; - -Point2i Point2i::operator/(const Point2i &p_v1) const { - - return Point2i(x / p_v1.x, y / p_v1.y); -}; - -Point2i Point2i::operator/(const int &rvalue) const { - - return Point2i(x / rvalue, y / rvalue); -}; - -void Point2i::operator/=(const int &rvalue) { - - x /= rvalue; - y /= rvalue; -}; - -Point2i Point2i::operator-() const { - - return Point2i(-x, -y); -} - -bool Point2i::operator==(const Point2i &p_vec2) const { - - return x == p_vec2.x && y == p_vec2.y; -} -bool Point2i::operator!=(const Point2i &p_vec2) const { - - return x != p_vec2.x || y != p_vec2.y; -} +#include "transform_2d.h" void Transform2D::invert() { // FIXME: this function assumes the basis is a rotation matrix, with no scaling. diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h new file mode 100644 index 0000000000..c8fc3c39e3 --- /dev/null +++ b/core/math/transform_2d.h @@ -0,0 +1,201 @@ +/*************************************************************************/ +/* transform_2d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TRANSFORM_2D_H +#define TRANSFORM_2D_H + +#include "core/math/rect2.h" // also includes vector2, math_funcs, and ustring + +struct Transform2D { + // Warning #1: basis of Transform2D is stored differently from Basis. In terms of elements array, the basis matrix looks like "on paper": + // M = (elements[0][0] elements[1][0]) + // (elements[0][1] elements[1][1]) + // This is such that the columns, which can be interpreted as basis vectors of the coordinate system "painted" on the object, can be accessed as elements[i]. + // Note that this is the opposite of the indices in mathematical texts, meaning: $M_{12}$ in a math book corresponds to elements[1][0] here. + // This requires additional care when working with explicit indices. + // See https://en.wikipedia.org/wiki/Row-_and_column-major_order for further reading. + + // Warning #2: 2D be aware that unlike 3D code, 2D code uses a left-handed coordinate system: Y-axis points down, + // and angle is measure from +X to +Y in a clockwise-fashion. + + Vector2 elements[3]; + + _FORCE_INLINE_ real_t tdotx(const Vector2 &v) const { return elements[0][0] * v.x + elements[1][0] * v.y; } + _FORCE_INLINE_ real_t tdoty(const Vector2 &v) const { return elements[0][1] * v.x + elements[1][1] * v.y; } + + const Vector2 &operator[](int p_idx) const { return elements[p_idx]; } + Vector2 &operator[](int p_idx) { return elements[p_idx]; } + + _FORCE_INLINE_ Vector2 get_axis(int p_axis) const { + ERR_FAIL_INDEX_V(p_axis, 3, Vector2()); + return elements[p_axis]; + } + _FORCE_INLINE_ void set_axis(int p_axis, const Vector2 &p_vec) { + ERR_FAIL_INDEX(p_axis, 3); + elements[p_axis] = p_vec; + } + + void invert(); + Transform2D inverse() const; + + void affine_invert(); + Transform2D affine_inverse() const; + + void set_rotation(real_t p_rot); + real_t get_rotation() const; + _FORCE_INLINE_ void set_rotation_and_scale(real_t p_rot, const Size2 &p_scale); + void rotate(real_t p_phi); + + void scale(const Size2 &p_scale); + void scale_basis(const Size2 &p_scale); + void translate(real_t p_tx, real_t p_ty); + void translate(const Vector2 &p_translation); + + real_t basis_determinant() const; + + Size2 get_scale() const; + + _FORCE_INLINE_ const Vector2 &get_origin() const { return elements[2]; } + _FORCE_INLINE_ void set_origin(const Vector2 &p_origin) { elements[2] = p_origin; } + + Transform2D scaled(const Size2 &p_scale) const; + Transform2D basis_scaled(const Size2 &p_scale) const; + Transform2D translated(const Vector2 &p_offset) const; + Transform2D rotated(real_t p_phi) const; + + Transform2D untranslated() const; + + void orthonormalize(); + Transform2D orthonormalized() const; + + bool operator==(const Transform2D &p_transform) const; + bool operator!=(const Transform2D &p_transform) const; + + void operator*=(const Transform2D &p_transform); + Transform2D operator*(const Transform2D &p_transform) const; + + Transform2D interpolate_with(const Transform2D &p_transform, real_t p_c) const; + + _FORCE_INLINE_ Vector2 basis_xform(const Vector2 &p_vec) const; + _FORCE_INLINE_ Vector2 basis_xform_inv(const Vector2 &p_vec) const; + _FORCE_INLINE_ Vector2 xform(const Vector2 &p_vec) const; + _FORCE_INLINE_ Vector2 xform_inv(const Vector2 &p_vec) const; + _FORCE_INLINE_ Rect2 xform(const Rect2 &p_rect) const; + _FORCE_INLINE_ Rect2 xform_inv(const Rect2 &p_rect) const; + + operator String() const; + + Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy) { + + elements[0][0] = xx; + elements[0][1] = xy; + elements[1][0] = yx; + elements[1][1] = yy; + elements[2][0] = ox; + elements[2][1] = oy; + } + + Transform2D(real_t p_rot, const Vector2 &p_pos); + Transform2D() { + elements[0][0] = 1.0; + elements[1][1] = 1.0; + } +}; + +Vector2 Transform2D::basis_xform(const Vector2 &p_vec) const { + + return Vector2( + tdotx(p_vec), + tdoty(p_vec)); +} + +Vector2 Transform2D::basis_xform_inv(const Vector2 &p_vec) const { + + return Vector2( + elements[0].dot(p_vec), + elements[1].dot(p_vec)); +} + +Vector2 Transform2D::xform(const Vector2 &p_vec) const { + + return Vector2( + tdotx(p_vec), + tdoty(p_vec)) + + elements[2]; +} +Vector2 Transform2D::xform_inv(const Vector2 &p_vec) const { + + Vector2 v = p_vec - elements[2]; + + return Vector2( + elements[0].dot(v), + elements[1].dot(v)); +} +Rect2 Transform2D::xform(const Rect2 &p_rect) const { + + Vector2 x = elements[0] * p_rect.size.x; + Vector2 y = elements[1] * p_rect.size.y; + Vector2 pos = xform(p_rect.position); + + Rect2 new_rect; + new_rect.position = pos; + new_rect.expand_to(pos + x); + new_rect.expand_to(pos + y); + new_rect.expand_to(pos + x + y); + return new_rect; +} + +void Transform2D::set_rotation_and_scale(real_t p_rot, const Size2 &p_scale) { + + elements[0][0] = Math::cos(p_rot) * p_scale.x; + elements[1][1] = Math::cos(p_rot) * p_scale.y; + elements[1][0] = -Math::sin(p_rot) * p_scale.y; + elements[0][1] = Math::sin(p_rot) * p_scale.x; +} + +Rect2 Transform2D::xform_inv(const Rect2 &p_rect) const { + + Vector2 ends[4] = { + xform_inv(p_rect.position), + xform_inv(Vector2(p_rect.position.x, p_rect.position.y + p_rect.size.y)), + xform_inv(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y + p_rect.size.y)), + xform_inv(Vector2(p_rect.position.x + p_rect.size.x, p_rect.position.y)) + }; + + Rect2 new_rect; + new_rect.position = ends[0]; + new_rect.expand_to(ends[1]); + new_rect.expand_to(ends[2]); + new_rect.expand_to(ends[3]); + + return new_rect; +} + +#endif // TRANSFORM_2D_H diff --git a/core/math/triangle_mesh.cpp b/core/math/triangle_mesh.cpp index 5475f733c3..6b8dc5eeb3 100644 --- a/core/math/triangle_mesh.cpp +++ b/core/math/triangle_mesh.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "triangle_mesh.h" -#include "sort.h" + +#include "core/sort.h" int TriangleMesh::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, int p_depth, int &max_depth, int &max_alloc) { diff --git a/core/math/triangle_mesh.h b/core/math/triangle_mesh.h index bf793fc50f..e5f181fba7 100644 --- a/core/math/triangle_mesh.h +++ b/core/math/triangle_mesh.h @@ -31,8 +31,9 @@ #ifndef TRIANGLE_MESH_H #define TRIANGLE_MESH_H -#include "face3.h" -#include "reference.h" +#include "core/math/face3.h" +#include "core/reference.h" + class TriangleMesh : public Reference { GDCLASS(TriangleMesh, Reference); diff --git a/core/math/triangulate.cpp b/core/math/triangulate.cpp index 0edc0ea039..69ffc95946 100644 --- a/core/math/triangulate.cpp +++ b/core/math/triangulate.cpp @@ -186,7 +186,7 @@ bool Triangulate::triangulate(const Vector<Vector2> &contour, Vector<int> &resul nv--; - /* resest error detection counter */ + /* reset error detection counter */ count = 2 * nv; } } diff --git a/core/math/triangulate.h b/core/math/triangulate.h index b1a583d0c5..2b0557ee55 100644 --- a/core/math/triangulate.h +++ b/core/math/triangulate.h @@ -31,7 +31,7 @@ #ifndef TRIANGULATE_H #define TRIANGULATE_H -#include "math_2d.h" +#include "core/math/vector2.h" /* http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp new file mode 100644 index 0000000000..7c6f056f09 --- /dev/null +++ b/core/math/vector2.cpp @@ -0,0 +1,250 @@ +/*************************************************************************/ +/* vector2.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "vector2.h" + +real_t Vector2::angle() const { + + return Math::atan2(y, x); +} + +real_t Vector2::length() const { + + return Math::sqrt(x * x + y * y); +} + +real_t Vector2::length_squared() const { + + return x * x + y * y; +} + +void Vector2::normalize() { + + real_t l = x * x + y * y; + if (l != 0) { + + l = Math::sqrt(l); + x /= l; + y /= l; + } +} + +Vector2 Vector2::normalized() const { + + Vector2 v = *this; + v.normalize(); + return v; +} + +bool Vector2::is_normalized() const { + // use length_squared() instead of length() to avoid sqrt(), makes it more stringent. + return Math::is_equal_approx(length_squared(), 1.0); +} + +real_t Vector2::distance_to(const Vector2 &p_vector2) const { + + return Math::sqrt((x - p_vector2.x) * (x - p_vector2.x) + (y - p_vector2.y) * (y - p_vector2.y)); +} + +real_t Vector2::distance_squared_to(const Vector2 &p_vector2) const { + + return (x - p_vector2.x) * (x - p_vector2.x) + (y - p_vector2.y) * (y - p_vector2.y); +} + +real_t Vector2::angle_to(const Vector2 &p_vector2) const { + + return Math::atan2(cross(p_vector2), dot(p_vector2)); +} + +real_t Vector2::angle_to_point(const Vector2 &p_vector2) const { + + return Math::atan2(y - p_vector2.y, x - p_vector2.x); +} + +real_t Vector2::dot(const Vector2 &p_other) const { + + return x * p_other.x + y * p_other.y; +} + +real_t Vector2::cross(const Vector2 &p_other) const { + + return x * p_other.y - y * p_other.x; +} + +Vector2 Vector2::floor() const { + + return Vector2(Math::floor(x), Math::floor(y)); +} + +Vector2 Vector2::ceil() const { + + return Vector2(Math::ceil(x), Math::ceil(y)); +} + +Vector2 Vector2::round() const { + + return Vector2(Math::round(x), Math::round(y)); +} + +Vector2 Vector2::rotated(real_t p_by) const { + + Vector2 v; + v.set_rotation(angle() + p_by); + v *= length(); + return v; +} + +Vector2 Vector2::project(const Vector2 &p_b) const { + return p_b * (dot(p_b) / p_b.length_squared()); +} + +Vector2 Vector2::snapped(const Vector2 &p_by) const { + + return Vector2( + Math::stepify(x, p_by.x), + Math::stepify(y, p_by.y)); +} + +Vector2 Vector2::clamped(real_t p_len) const { + + real_t l = length(); + Vector2 v = *this; + if (l > 0 && p_len < l) { + + v /= l; + v *= p_len; + } + + return v; +} + +Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_t) const { + + Vector2 p0 = p_pre_a; + Vector2 p1 = *this; + Vector2 p2 = p_b; + Vector2 p3 = p_post_b; + + real_t t = p_t; + real_t t2 = t * t; + real_t t3 = t2 * t; + + Vector2 out; + out = 0.5 * ((p1 * 2.0) + + (-p0 + p2) * t + + (2.0 * p0 - 5.0 * p1 + 4 * p2 - p3) * t2 + + (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3); + return out; +} + +// slide returns the component of the vector along the given plane, specified by its normal vector. +Vector2 Vector2::slide(const Vector2 &p_normal) const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V(!p_normal.is_normalized(), Vector2()); +#endif + return *this - p_normal * this->dot(p_normal); +} + +Vector2 Vector2::bounce(const Vector2 &p_normal) const { + return -reflect(p_normal); +} + +Vector2 Vector2::reflect(const Vector2 &p_normal) const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V(!p_normal.is_normalized(), Vector2()); +#endif + return 2.0 * p_normal * this->dot(p_normal) - *this; +} + +/* Vector2i */ + +Vector2i Vector2i::operator+(const Vector2i &p_v) const { + + return Vector2i(x + p_v.x, y + p_v.y); +} +void Vector2i::operator+=(const Vector2i &p_v) { + + x += p_v.x; + y += p_v.y; +} +Vector2i Vector2i::operator-(const Vector2i &p_v) const { + + return Vector2i(x - p_v.x, y - p_v.y); +} +void Vector2i::operator-=(const Vector2i &p_v) { + + x -= p_v.x; + y -= p_v.y; +} + +Vector2i Vector2i::operator*(const Vector2i &p_v1) const { + + return Vector2i(x * p_v1.x, y * p_v1.y); +}; + +Vector2i Vector2i::operator*(const int &rvalue) const { + + return Vector2i(x * rvalue, y * rvalue); +}; +void Vector2i::operator*=(const int &rvalue) { + + x *= rvalue; + y *= rvalue; +}; + +Vector2i Vector2i::operator/(const Vector2i &p_v1) const { + + return Vector2i(x / p_v1.x, y / p_v1.y); +}; + +Vector2i Vector2i::operator/(const int &rvalue) const { + + return Vector2i(x / rvalue, y / rvalue); +}; + +void Vector2i::operator/=(const int &rvalue) { + + x /= rvalue; + y /= rvalue; +}; + +Vector2i Vector2i::operator-() const { + + return Vector2i(-x, -y); +} + +bool Vector2i::operator==(const Vector2i &p_vec2) const { + + return x == p_vec2.x && y == p_vec2.y; +} +bool Vector2i::operator!=(const Vector2i &p_vec2) const { + + return x != p_vec2.x || y != p_vec2.y; +} diff --git a/core/math/vector2.h b/core/math/vector2.h new file mode 100644 index 0000000000..e5e555597d --- /dev/null +++ b/core/math/vector2.h @@ -0,0 +1,316 @@ +/*************************************************************************/ +/* vector2.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef VECTOR2_H +#define VECTOR2_H + +#include "core/math/math_funcs.h" +#include "core/ustring.h" + +struct Vector2i; + +struct Vector2 { + + union { + real_t x; + real_t width; + }; + union { + real_t y; + real_t height; + }; + + _FORCE_INLINE_ real_t &operator[](int p_idx) { + return p_idx ? y : x; + } + _FORCE_INLINE_ const real_t &operator[](int p_idx) const { + return p_idx ? y : x; + } + + void normalize(); + Vector2 normalized() const; + bool is_normalized() const; + + real_t length() const; + real_t length_squared() const; + + real_t distance_to(const Vector2 &p_vector2) const; + real_t distance_squared_to(const Vector2 &p_vector2) const; + real_t angle_to(const Vector2 &p_vector2) const; + real_t angle_to_point(const Vector2 &p_vector2) const; + + real_t dot(const Vector2 &p_other) const; + real_t cross(const Vector2 &p_other) const; + Vector2 project(const Vector2 &p_b) const; + + Vector2 plane_project(real_t p_d, const Vector2 &p_vec) const; + + Vector2 clamped(real_t p_len) const; + + _FORCE_INLINE_ static Vector2 linear_interpolate(const Vector2 &p_a, const Vector2 &p_b, real_t p_t); + _FORCE_INLINE_ Vector2 linear_interpolate(const Vector2 &p_b, real_t p_t) const; + _FORCE_INLINE_ Vector2 slerp(const Vector2 &p_b, real_t p_t) const; + Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_t) const; + + Vector2 slide(const Vector2 &p_normal) const; + Vector2 bounce(const Vector2 &p_normal) const; + Vector2 reflect(const Vector2 &p_normal) const; + + Vector2 operator+(const Vector2 &p_v) const; + void operator+=(const Vector2 &p_v); + Vector2 operator-(const Vector2 &p_v) const; + void operator-=(const Vector2 &p_v); + Vector2 operator*(const Vector2 &p_v1) const; + + Vector2 operator*(const real_t &rvalue) const; + void operator*=(const real_t &rvalue); + void operator*=(const Vector2 &rvalue) { *this = *this * rvalue; } + + Vector2 operator/(const Vector2 &p_v1) const; + + Vector2 operator/(const real_t &rvalue) const; + + void operator/=(const real_t &rvalue); + + Vector2 operator-() const; + + bool operator==(const Vector2 &p_vec2) const; + bool operator!=(const Vector2 &p_vec2) const; + + bool operator<(const Vector2 &p_vec2) const { return (x == p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); } + bool operator<=(const Vector2 &p_vec2) const { return (x == p_vec2.x) ? (y <= p_vec2.y) : (x <= p_vec2.x); } + + real_t angle() const; + + void set_rotation(real_t p_radians) { + + x = Math::cos(p_radians); + y = Math::sin(p_radians); + } + + _FORCE_INLINE_ Vector2 abs() const { + + return Vector2(Math::abs(x), Math::abs(y)); + } + + Vector2 rotated(real_t p_by) const; + Vector2 tangent() const { + + return Vector2(y, -x); + } + + Vector2 floor() const; + Vector2 ceil() const; + Vector2 round() const; + Vector2 snapped(const Vector2 &p_by) const; + real_t aspect() const { return width / height; } + + operator String() const { return String::num(x) + ", " + String::num(y); } + + _FORCE_INLINE_ Vector2(real_t p_x, real_t p_y) { + x = p_x; + y = p_y; + } + _FORCE_INLINE_ Vector2() { + x = 0; + y = 0; + } +}; + +_FORCE_INLINE_ Vector2 Vector2::plane_project(real_t p_d, const Vector2 &p_vec) const { + + return p_vec - *this * (dot(p_vec) - p_d); +} + +_FORCE_INLINE_ Vector2 operator*(real_t p_scalar, const Vector2 &p_vec) { + + return p_vec * p_scalar; +} + +_FORCE_INLINE_ Vector2 Vector2::operator+(const Vector2 &p_v) const { + + return Vector2(x + p_v.x, y + p_v.y); +} +_FORCE_INLINE_ void Vector2::operator+=(const Vector2 &p_v) { + + x += p_v.x; + y += p_v.y; +} +_FORCE_INLINE_ Vector2 Vector2::operator-(const Vector2 &p_v) const { + + return Vector2(x - p_v.x, y - p_v.y); +} +_FORCE_INLINE_ void Vector2::operator-=(const Vector2 &p_v) { + + x -= p_v.x; + y -= p_v.y; +} + +_FORCE_INLINE_ Vector2 Vector2::operator*(const Vector2 &p_v1) const { + + return Vector2(x * p_v1.x, y * p_v1.y); +}; + +_FORCE_INLINE_ Vector2 Vector2::operator*(const real_t &rvalue) const { + + return Vector2(x * rvalue, y * rvalue); +}; +_FORCE_INLINE_ void Vector2::operator*=(const real_t &rvalue) { + + x *= rvalue; + y *= rvalue; +}; + +_FORCE_INLINE_ Vector2 Vector2::operator/(const Vector2 &p_v1) const { + + return Vector2(x / p_v1.x, y / p_v1.y); +}; + +_FORCE_INLINE_ Vector2 Vector2::operator/(const real_t &rvalue) const { + + return Vector2(x / rvalue, y / rvalue); +}; + +_FORCE_INLINE_ void Vector2::operator/=(const real_t &rvalue) { + + x /= rvalue; + y /= rvalue; +}; + +_FORCE_INLINE_ Vector2 Vector2::operator-() const { + + return Vector2(-x, -y); +} + +_FORCE_INLINE_ bool Vector2::operator==(const Vector2 &p_vec2) const { + + return x == p_vec2.x && y == p_vec2.y; +} +_FORCE_INLINE_ bool Vector2::operator!=(const Vector2 &p_vec2) const { + + return x != p_vec2.x || y != p_vec2.y; +} + +Vector2 Vector2::linear_interpolate(const Vector2 &p_b, real_t p_t) const { + + Vector2 res = *this; + + res.x += (p_t * (p_b.x - x)); + res.y += (p_t * (p_b.y - y)); + + return res; +} + +Vector2 Vector2::slerp(const Vector2 &p_b, real_t p_t) const { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V(!is_normalized(), Vector2()); +#endif + real_t theta = angle_to(p_b); + return rotated(theta * p_t); +} + +Vector2 Vector2::linear_interpolate(const Vector2 &p_a, const Vector2 &p_b, real_t p_t) { + + Vector2 res = p_a; + + res.x += (p_t * (p_b.x - p_a.x)); + res.y += (p_t * (p_b.y - p_a.y)); + + return res; +} + +typedef Vector2 Size2; +typedef Vector2 Point2; + +/* INTEGER STUFF */ + +struct Vector2i { + + union { + int x; + int width; + }; + union { + int y; + int height; + }; + + _FORCE_INLINE_ int &operator[](int p_idx) { + return p_idx ? y : x; + } + _FORCE_INLINE_ const int &operator[](int p_idx) const { + return p_idx ? y : x; + } + + Vector2i operator+(const Vector2i &p_v) const; + void operator+=(const Vector2i &p_v); + Vector2i operator-(const Vector2i &p_v) const; + void operator-=(const Vector2i &p_v); + Vector2i operator*(const Vector2i &p_v1) const; + + Vector2i operator*(const int &rvalue) const; + void operator*=(const int &rvalue); + + Vector2i operator/(const Vector2i &p_v1) const; + + Vector2i operator/(const int &rvalue) const; + + void operator/=(const int &rvalue); + + Vector2i operator-() const; + bool operator<(const Vector2i &p_vec2) const { return (x == p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); } + bool operator>(const Vector2i &p_vec2) const { return (x == p_vec2.x) ? (y > p_vec2.y) : (x > p_vec2.x); } + + bool operator==(const Vector2i &p_vec2) const; + bool operator!=(const Vector2i &p_vec2) const; + + real_t get_aspect() const { return width / (real_t)height; } + + operator String() const { return String::num(x) + ", " + String::num(y); } + + operator Vector2() const { return Vector2(x, y); } + inline Vector2i(const Vector2 &p_vec2) { + x = (int)p_vec2.x; + y = (int)p_vec2.y; + } + inline Vector2i(int p_x, int p_y) { + x = p_x; + y = p_y; + } + inline Vector2i() { + x = 0; + y = 0; + } +}; + +typedef Vector2i Size2i; +typedef Vector2i Point2i; + +#endif // VECTOR2_H diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp index 78d52d5cd1..5dbb01493d 100644 --- a/core/math/vector3.cpp +++ b/core/math/vector3.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "vector3.h" -#include "matrix3.h" + +#include "core/math/matrix3.h" void Vector3::rotate(const Vector3 &p_axis, real_t p_phi) { diff --git a/core/math/vector3.h b/core/math/vector3.h index 433adf09ee..16feba6a0c 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -31,10 +31,10 @@ #ifndef VECTOR3_H #define VECTOR3_H -#include "math_defs.h" -#include "math_funcs.h" -#include "typedefs.h" -#include "ustring.h" +#include "core/math/math_defs.h" +#include "core/math/math_funcs.h" +#include "core/typedefs.h" +#include "core/ustring.h" class Basis; @@ -109,6 +109,8 @@ struct Vector3 { _FORCE_INLINE_ real_t distance_to(const Vector3 &p_b) const; _FORCE_INLINE_ real_t distance_squared_to(const Vector3 &p_b) const; + _FORCE_INLINE_ Vector3 project(const Vector3 &p_b) const; + _FORCE_INLINE_ real_t angle_to(const Vector3 &p_b) const; _FORCE_INLINE_ Vector3 slide(const Vector3 &p_normal) const; @@ -148,13 +150,8 @@ struct Vector3 { } }; -#ifdef VECTOR3_IMPL_OVERRIDE - -#include "vector3_inline.h" - -#else - -#include "matrix3.h" +// Should be included after class definition, otherwise we get circular refs +#include "core/math/matrix3.h" Vector3 Vector3::cross(const Vector3 &p_b) const { @@ -221,7 +218,7 @@ Vector3 Vector3::linear_interpolate(const Vector3 &p_b, real_t p_t) const { Vector3 Vector3::slerp(const Vector3 &p_b, real_t p_t) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(is_normalized() == false, Vector3()); + ERR_FAIL_COND_V(!is_normalized(), Vector3()); #endif real_t theta = angle_to(p_b); @@ -238,6 +235,10 @@ real_t Vector3::distance_squared_to(const Vector3 &p_b) const { return (p_b - *this).length_squared(); } +Vector3 Vector3::project(const Vector3 &p_b) const { + return p_b * (dot(p_b) / p_b.length_squared()); +} + real_t Vector3::angle_to(const Vector3 &p_b) const { return Math::atan2(cross(p_b).length(), dot(p_b)); @@ -429,7 +430,7 @@ void Vector3::zero() { // slide returns the component of the vector along the given plane, specified by its normal vector. Vector3 Vector3::slide(const Vector3 &p_normal) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(p_normal.is_normalized() == false, Vector3()); + ERR_FAIL_COND_V(!p_normal.is_normalized(), Vector3()); #endif return *this - p_normal * this->dot(p_normal); } @@ -440,11 +441,9 @@ Vector3 Vector3::bounce(const Vector3 &p_normal) const { Vector3 Vector3::reflect(const Vector3 &p_normal) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V(p_normal.is_normalized() == false, Vector3()); + ERR_FAIL_COND_V(!p_normal.is_normalized(), Vector3()); #endif return 2.0 * p_normal * this->dot(p_normal) - *this; } -#endif - #endif // VECTOR3_H diff --git a/core/message_queue.cpp b/core/message_queue.cpp index 3adaad868a..2952593798 100644 --- a/core/message_queue.cpp +++ b/core/message_queue.cpp @@ -30,8 +30,8 @@ #include "message_queue.h" -#include "project_settings.h" -#include "script_language.h" +#include "core/project_settings.h" +#include "core/script_language.h" MessageQueue *MessageQueue::singleton = NULL; @@ -50,9 +50,9 @@ Error MessageQueue::push_call(ObjectID p_id, const StringName &p_method, const V String type; if (ObjectDB::get_instance(p_id)) type = ObjectDB::get_instance(p_id)->get_class(); - print_line("failed method: " + type + ":" + p_method + " target ID: " + itos(p_id)); + print_line("Failed method: " + type + ":" + p_method + " target ID: " + itos(p_id)); statistics(); - ERR_EXPLAIN("Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings"); + ERR_EXPLAIN("Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings."); ERR_FAIL_V(ERR_OUT_OF_MEMORY); } @@ -101,9 +101,9 @@ Error MessageQueue::push_set(ObjectID p_id, const StringName &p_prop, const Vari String type; if (ObjectDB::get_instance(p_id)) type = ObjectDB::get_instance(p_id)->get_class(); - print_line("failed set: " + type + ":" + p_prop + " target ID: " + itos(p_id)); + print_line("Failed set: " + type + ":" + p_prop + " target ID: " + itos(p_id)); statistics(); - ERR_EXPLAIN("Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings"); + ERR_EXPLAIN("Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings."); ERR_FAIL_V(ERR_OUT_OF_MEMORY); } @@ -134,9 +134,9 @@ Error MessageQueue::push_notification(ObjectID p_id, int p_notification) { String type; if (ObjectDB::get_instance(p_id)) type = ObjectDB::get_instance(p_id)->get_class(); - print_line("failed notification: " + itos(p_notification) + " target ID: " + itos(p_id)); + print_line("Failed notification: " + itos(p_notification) + " target ID: " + itos(p_id)); statistics(); - ERR_EXPLAIN("Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings"); + ERR_EXPLAIN("Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings."); ERR_FAIL_V(ERR_OUT_OF_MEMORY); } @@ -209,10 +209,9 @@ void MessageQueue::statistics() { } break; } - //object was deleted - //WARN_PRINT("Object was deleted while awaiting a callback") - //should it print a warning? } else { + //object was deleted + print_line("Object was deleted while awaiting a callback"); null_count++; } @@ -226,17 +225,14 @@ void MessageQueue::statistics() { print_line("NULL count: " + itos(null_count)); for (Map<StringName, int>::Element *E = set_count.front(); E; E = E->next()) { - print_line("SET " + E->key() + ": " + itos(E->get())); } for (Map<StringName, int>::Element *E = call_count.front(); E; E = E->next()) { - print_line("CALL " + E->key() + ": " + itos(E->get())); } for (Map<int, int>::Element *E = notify_count.front(); E; E = E->next()) { - print_line("NOTIFY " + itos(E->key()) + ": " + itos(E->get())); } } @@ -268,7 +264,6 @@ void MessageQueue::flush() { if (buffer_end > buffer_max_used) { buffer_max_used = buffer_end; - //statistics(); } uint32_t read_pos = 0; @@ -343,6 +338,7 @@ MessageQueue::MessageQueue() { buffer_end = 0; buffer_max_used = 0; buffer_size = GLOBAL_DEF_RST("memory/limits/message_queue/max_size_kb", DEFAULT_QUEUE_SIZE_KB); + ProjectSettings::get_singleton()->set_custom_property_info("memory/limits/message_queue/max_size_kb", PropertyInfo(Variant::INT, "memory/limits/message_queue/max_size_kb", PROPERTY_HINT_RANGE, "0,2048,1,or_greater")); buffer_size *= 1024; buffer = memnew_arr(uint8_t, buffer_size); } diff --git a/core/message_queue.h b/core/message_queue.h index be5ffe4fae..2083bb0639 100644 --- a/core/message_queue.h +++ b/core/message_queue.h @@ -31,9 +31,8 @@ #ifndef MESSAGE_QUEUE_H #define MESSAGE_QUEUE_H -#include "object.h" -#include "os/mutex.h" -#include "os/thread_safe.h" +#include "core/object.h" +#include "core/os/thread_safe.h" class MessageQueue { @@ -44,8 +43,6 @@ class MessageQueue { DEFAULT_QUEUE_SIZE_KB = 1024 }; - Mutex *mutex; - enum { TYPE_CALL, TYPE_NOTIFICATION, diff --git a/core/method_bind.cpp b/core/method_bind.cpp index 52ee9e0848..fa5b88b5ac 100644 --- a/core/method_bind.cpp +++ b/core/method_bind.cpp @@ -30,7 +30,7 @@ // object.h needs to be the first include *before* method_bind.h // FIXME: Find out why and fix potential cyclical dependencies. -#include "object.h" +#include "core/object.h" #include "method_bind.h" diff --git a/core/method_bind.h b/core/method_bind.h index 7ee687ee40..7788de919c 100644 --- a/core/method_bind.h +++ b/core/method_bind.h @@ -31,10 +31,11 @@ #ifndef METHOD_BIND_H #define METHOD_BIND_H -#include "list.h" -#include "method_ptrcall.h" -#include "object.h" -#include "variant.h" +#include "core/list.h" +#include "core/method_ptrcall.h" +#include "core/object.h" +#include "core/variant.h" + #include <stdio.h> /** @@ -45,7 +46,7 @@ #define DEBUG_METHODS_ENABLED #endif -#include "type_info.h" +#include "core/type_info.h" enum MethodFlags { diff --git a/core/method_ptrcall.h b/core/method_ptrcall.h index 2f6dcb3178..b706e65d53 100644 --- a/core/method_ptrcall.h +++ b/core/method_ptrcall.h @@ -31,9 +31,9 @@ #ifndef METHOD_PTRCALL_H #define METHOD_PTRCALL_H -#include "math_2d.h" -#include "typedefs.h" -#include "variant.h" +#include "core/math/transform_2d.h" +#include "core/typedefs.h" +#include "core/variant.h" #ifdef PTRCALL_ENABLED diff --git a/core/node_path.cpp b/core/node_path.cpp index 7d4116fa1e..91e2aa5f4e 100644 --- a/core/node_path.cpp +++ b/core/node_path.cpp @@ -30,7 +30,7 @@ #include "node_path.h" -#include "print_string.h" +#include "core/print_string.h" void NodePath::_update_hash_cache() const { @@ -276,7 +276,7 @@ NodePath NodePath::get_as_property_path() const { String initial_subname = data->path[0]; - for (size_t i = 1; i < data->path.size(); i++) { + for (int i = 1; i < data->path.size(); i++) { initial_subname += "/" + data->path[i]; } new_path.insert(0, initial_subname); diff --git a/core/node_path.h b/core/node_path.h index 71235029af..4a72d4a4d5 100644 --- a/core/node_path.h +++ b/core/node_path.h @@ -31,8 +31,9 @@ #ifndef NODE_PATH_H #define NODE_PATH_H -#include "string_db.h" -#include "ustring.h" +#include "core/string_db.h" +#include "core/ustring.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ diff --git a/core/oa_hash_map.h b/core/oa_hash_map.h index 0b3b40f30c..9840442519 100644 --- a/core/oa_hash_map.h +++ b/core/oa_hash_map.h @@ -31,10 +31,10 @@ #ifndef OA_HASH_MAP_H #define OA_HASH_MAP_H -#include "hashfuncs.h" -#include "math_funcs.h" -#include "os/copymem.h" -#include "os/memory.h" +#include "core/hashfuncs.h" +#include "core/math/math_funcs.h" +#include "core/os/copymem.h" +#include "core/os/memory.h" /** * A HashMap implementation that uses open addressing with robinhood hashing. @@ -125,7 +125,7 @@ private: while (42) { if (hashes[pos] == EMPTY_HASH) { - _construct(pos, hash, p_key, p_value); + _construct(pos, hash, key, value); return; } @@ -136,7 +136,7 @@ private: if (hashes[pos] & DELETED_HASH_BIT) { // we found a place where we can fit in! - _construct(pos, hash, p_key, p_value); + _construct(pos, hash, key, value); return; } @@ -166,7 +166,7 @@ private: values = memnew_arr(TValue, capacity); hashes = memnew_arr(uint32_t, capacity); - for (int i = 0; i < capacity; i++) { + for (uint32_t i = 0; i < capacity; i++) { hashes[i] = 0; } @@ -311,7 +311,7 @@ public: values = memnew_arr(TValue, p_initial_capacity); hashes = memnew_arr(uint32_t, p_initial_capacity); - for (int i = 0; i < p_initial_capacity; i++) { + for (uint32_t i = 0; i < p_initial_capacity; i++) { hashes[i] = 0; } } @@ -320,7 +320,7 @@ public: memdelete_arr(keys); memdelete_arr(values); - memdelete(hashes); + memdelete_arr(hashes); } }; diff --git a/core/object.cpp b/core/object.cpp index a0c64feb09..3a14c7c0b5 100644 --- a/core/object.cpp +++ b/core/object.cpp @@ -30,14 +30,14 @@ #include "object.h" -#include "class_db.h" -#include "core_string_names.h" -#include "message_queue.h" -#include "os/os.h" -#include "print_string.h" -#include "resource.h" -#include "script_language.h" -#include "translation.h" +#include "core/class_db.h" +#include "core/core_string_names.h" +#include "core/message_queue.h" +#include "core/os/os.h" +#include "core/print_string.h" +#include "core/resource.h" +#include "core/script_language.h" +#include "core/translation.h" #ifdef DEBUG_ENABLED @@ -277,8 +277,8 @@ MethodInfo::MethodInfo(Variant::Type ret, const String &p_name, const PropertyIn MethodInfo::MethodInfo(const PropertyInfo &p_ret, const String &p_name) : name(p_name), - flags(METHOD_FLAG_NORMAL), return_val(p_ret), + flags(METHOD_FLAG_NORMAL), id(0) { } @@ -440,26 +440,41 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid if (r_valid) *r_valid = true; return; -#ifdef TOOLS_ENABLED - } else if (p_name == CoreStringNames::get_singleton()->_sections_unfolded) { - Array arr = p_value; - for (int i = 0; i < arr.size(); i++) { - editor_section_folding.insert(arr[i]); - } + } + + //something inside the object... :| + bool success = _setv(p_name, p_value); + if (success) { if (r_valid) *r_valid = true; return; -#endif - } else { - //something inside the object... :| - bool success = _setv(p_name, p_value); - if (success) { + } + + { + bool valid; + setvar(p_name, p_value, &valid); + if (valid) { + if (r_valid) + *r_valid = true; + return; + } + } + +#ifdef TOOLS_ENABLED + if (script_instance) { + bool valid; + script_instance->property_set_fallback(p_name, p_value, &valid); + if (valid) { if (r_valid) *r_valid = true; return; } - setvar(p_name, p_value, r_valid); } +#endif + + if (r_valid) + *r_valid = false; + return; } Variant Object::get(const StringName &p_name, bool *r_valid) const { @@ -495,16 +510,7 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const { if (r_valid) *r_valid = true; return ret; -#ifdef TOOLS_ENABLED - } else if (p_name == CoreStringNames::get_singleton()->_sections_unfolded) { - Array array; - for (Set<String>::Element *E = editor_section_folding.front(); E; E = E->next()) { - array.push_back(E->get()); - } - if (r_valid) - *r_valid = true; - return array; -#endif + } else { //something inside the object... :| bool success = _getv(p_name, ret); @@ -513,8 +519,33 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const { *r_valid = true; return ret; } + //if nothing else, use getvar - return getvar(p_name, r_valid); + { + bool valid; + ret = getvar(p_name, &valid); + if (valid) { + if (r_valid) + *r_valid = true; + return ret; + } + } + +#ifdef TOOLS_ENABLED + if (script_instance) { + bool valid; + ret = script_instance->property_get_fallback(p_name, &valid); + if (valid) { + if (r_valid) + *r_valid = true; + return ret; + } + } +#endif + + if (r_valid) + *r_valid = false; + return Variant(); } } @@ -605,15 +636,11 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons #ifdef TOOLS_ENABLED p_list->push_back(PropertyInfo(Variant::NIL, "Script", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); #endif - p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NONZERO)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT)); } -#ifdef TOOLS_ENABLED - if (editor_section_folding.size()) { - p_list->push_back(PropertyInfo(Variant::ARRAY, CoreStringNames::get_singleton()->_sections_unfolded, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + if (!metadata.empty()) { + p_list->push_back(PropertyInfo(Variant::DICTIONARY, "__meta__", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); } -#endif - if (!metadata.empty()) - p_list->push_back(PropertyInfo(Variant::DICTIONARY, "__meta__", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_STORE_IF_NONZERO)); if (script_instance && !p_reversed) { p_list->push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY)); script_instance->get_property_list(p_list); @@ -979,9 +1006,14 @@ void Object::set_script(const RefPtr &p_script) { script = p_script; Ref<Script> s(script); - if (!s.is_null() && s->can_instance()) { - OBJ_DEBUG_LOCK - script_instance = s->instance_create(this); + if (!s.is_null()) { + if (s->can_instance()) { + OBJ_DEBUG_LOCK + script_instance = s->instance_create(this); + } else if (Engine::get_singleton()->is_editor_hint()) { + OBJ_DEBUG_LOCK + script_instance = s->placeholder_instance_create(this); + } } _change_notify("script"); @@ -1199,7 +1231,10 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int target->call(c.method, args, argc, ce); if (ce.error != Variant::CallError::CALL_OK) { - +#ifdef DEBUG_ENABLED + if (c.flags & CONNECT_PERSIST && Engine::get_singleton()->is_editor_hint() && (script.is_null() || !Ref<Script>(script)->is_tool())) + continue; +#endif if (ce.error == Variant::CallError::CALL_ERROR_INVALID_METHOD && !ClassDB::class_exists(target->get_class_name())) { //most likely object is not initialized yet, do not throw error. } else { @@ -1408,8 +1443,20 @@ Error Object::connect(const StringName &p_signal, Object *p_to_object, const Str if (!s) { bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal); //check in script - if (!signal_is_valid && !script.is_null() && Ref<Script>(script)->has_script_signal(p_signal)) - signal_is_valid = true; + if (!signal_is_valid && !script.is_null()) { + + if (Ref<Script>(script)->has_script_signal(p_signal)) { + signal_is_valid = true; + } +#ifdef TOOLS_ENABLED + else { + //allow connecting signals anyway if script is invalid, see issue #17070 + if (!Ref<Script>(script)->is_valid()) { + signal_is_valid = true; + } + } +#endif + } if (!signal_is_valid) { ERR_EXPLAIN("In Object of type '" + String(get_class()) + "': Attempt to connect nonexistent signal '" + p_signal + "' to method '" + p_to_object->get_class() + "." + p_to_method + "'"); @@ -1421,8 +1468,13 @@ Error Object::connect(const StringName &p_signal, Object *p_to_object, const Str Signal::Target target(p_to_object->get_instance_id(), p_to_method); if (s->slot_map.has(target)) { - ERR_EXPLAIN("Signal '" + p_signal + "' is already connected to given method '" + p_to_method + "' in that object."); - ERR_FAIL_COND_V(s->slot_map.has(target), ERR_INVALID_PARAMETER); + if (p_flags & CONNECT_REFERENCE_COUNTED) { + s->slot_map[target].reference_count++; + return OK; + } else { + ERR_EXPLAIN("Signal '" + p_signal + "' is already connected to given method '" + p_to_method + "' in that object."); + ERR_FAIL_COND_V(s->slot_map.has(target), ERR_INVALID_PARAMETER); + } } Signal::Slot slot; @@ -1436,6 +1488,10 @@ Error Object::connect(const StringName &p_signal, Object *p_to_object, const Str conn.binds = p_binds; slot.conn = conn; slot.cE = p_to_object->connections.push_back(conn); + if (p_flags & CONNECT_REFERENCE_COUNTED) { + slot.reference_count = 1; + } + s->slot_map[target] = slot; return OK; @@ -1466,6 +1522,10 @@ bool Object::is_connected(const StringName &p_signal, Object *p_to_object, const void Object::disconnect(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method) { + _disconnect(p_signal, p_to_object, p_to_method); +} +void Object::_disconnect(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method, bool p_force) { + ERR_FAIL_NULL(p_to_object); Signal *s = signal_map.getptr(p_signal); if (!s) { @@ -1484,7 +1544,16 @@ void Object::disconnect(const StringName &p_signal, Object *p_to_object, const S ERR_FAIL(); } - p_to_object->connections.erase(s->slot_map[target].cE); + Signal::Slot *slot = &s->slot_map[target]; + + if (!p_force) { + slot->reference_count--; // by default is zero, if it was not referenced it will go below it + if (slot->reference_count >= 0) { + return; + } + } + + p_to_object->connections.erase(slot->cE); s->slot_map.erase(target); if (s->slot_map.empty() && ClassDB::has_signal(get_class_name(), p_signal)) { @@ -1658,6 +1727,8 @@ void Object::_bind_methods() { ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "call_deferred", &Object::_call_deferred_bind, mi); } + ClassDB::bind_method(D_METHOD("set_deferred", "property", "value"), &Object::set_deferred); + ClassDB::bind_method(D_METHOD("callv", "method", "arg_array"), &Object::callv); ClassDB::bind_method(D_METHOD("has_method", "method"), &Object::has_method); @@ -1706,6 +1777,7 @@ void Object::_bind_methods() { BIND_ENUM_CONSTANT(CONNECT_DEFERRED); BIND_ENUM_CONSTANT(CONNECT_PERSIST); BIND_ENUM_CONSTANT(CONNECT_ONESHOT); + BIND_ENUM_CONSTANT(CONNECT_REFERENCE_COUNTED); } void Object::call_deferred(const StringName &p_method, VARIANT_ARG_DECLARE) { @@ -1713,6 +1785,10 @@ void Object::call_deferred(const StringName &p_method, VARIANT_ARG_DECLARE) { MessageQueue::get_singleton()->push_call(this, p_method, VARIANT_ARG_PASS); } +void Object::set_deferred(const StringName &p_property, const Variant &p_value) { + MessageQueue::get_singleton()->push_set(this, p_property, p_value); +} + void Object::set_block_signals(bool p_block) { _block_signals = p_block; @@ -1838,7 +1914,11 @@ void *Object::get_script_instance_binding(int p_script_language_index) { //as it should not really affect performance much (won't be called too often), as in far most caes the condition below will be false afterwards if (!_script_instance_bindings[p_script_language_index]) { - _script_instance_bindings[p_script_language_index] = ScriptServer::get_language(p_script_language_index)->alloc_instance_binding_data(this); + void *script_data = ScriptServer::get_language(p_script_language_index)->alloc_instance_binding_data(this); + if (script_data) { + atomic_increment(&instance_binding_count); + _script_instance_bindings[p_script_language_index] = script_data; + } } return _script_instance_bindings[p_script_language_index]; @@ -1853,6 +1933,7 @@ Object::Object() { _instance_ID = ObjectDB::add_instance(this); _can_translate = true; _is_queued_for_deletion = false; + instance_binding_count = 0; memset(_script_instance_bindings, 0, sizeof(void *) * MAX_SCRIPT_INSTANCE_BINDINGS); script_instance = NULL; #ifdef TOOLS_ENABLED @@ -1872,34 +1953,34 @@ Object::~Object() { memdelete(script_instance); script_instance = NULL; - List<Connection> sconnections; const StringName *S = NULL; - while ((S = signal_map.next(S))) { + while ((S = signal_map.next(NULL))) { Signal *s = &signal_map[*S]; - ERR_EXPLAIN("Attempt to delete an object in the middle of a signal emission from it"); - ERR_CONTINUE(s->lock > 0); - - for (int i = 0; i < s->slot_map.size(); i++) { - - sconnections.push_back(s->slot_map.getv(i).conn); + if (s->lock) { + ERR_EXPLAIN("Attempt to delete an object in the middle of a signal emission from it"); + ERR_CONTINUE(s->lock > 0); } - } - for (List<Connection>::Element *E = sconnections.front(); E; E = E->next()) { + //brute force disconnect for performance + int slot_count = s->slot_map.size(); + const VMap<Signal::Target, Signal::Slot>::Pair *slot_list = s->slot_map.get_array(); - Connection &c = E->get(); - ERR_CONTINUE(c.source != this); //bug? + for (int i = 0; i < slot_count; i++) { + + slot_list[i].value.conn.target->connections.erase(slot_list[i].value.cE); + } - this->disconnect(c.signal, c.target, c.method); + signal_map.erase(*S); } + //signals from nodes that connect to this node while (connections.size()) { Connection c = connections.front()->get(); - c.source->disconnect(c.signal, c.target, c.method); + c.source->_disconnect(c.signal, c.target, c.method, true); } ObjectDB::remove_instance(this); @@ -1931,11 +2012,13 @@ ObjectID ObjectDB::add_instance(Object *p_object) { ERR_FAIL_COND_V(p_object->get_instance_id() != 0, 0); rw_lock->write_lock(); - instances[++instance_counter] = p_object; - instance_checks[p_object] = instance_counter; + ObjectID instance_id = ++instance_counter; + instances[instance_id] = p_object; + instance_checks[p_object] = instance_id; + rw_lock->write_unlock(); - return instance_counter; + return instance_id; } void ObjectDB::remove_instance(Object *p_object) { @@ -2002,16 +2085,15 @@ void ObjectDB::cleanup() { String node_name; if (instances[*K]->is_class("Node")) - node_name = " - Node Name: " + String(instances[*K]->call("get_name")); + node_name = " - Node name: " + String(instances[*K]->call("get_name")); if (instances[*K]->is_class("Resource")) - node_name = " - Resource Name: " + String(instances[*K]->call("get_name")) + " Path: " + String(instances[*K]->call("get_path")); - print_line("Leaked Instance: " + String(instances[*K]->get_class()) + ":" + itos(*K) + node_name); + node_name = " - Resource name: " + String(instances[*K]->call("get_name")) + " Path: " + String(instances[*K]->call("get_path")); + print_line("Leaked instance: " + String(instances[*K]->get_class()) + ":" + itos(*K) + node_name); } } } instances.clear(); instance_checks.clear(); rw_lock->write_unlock(); - memdelete(rw_lock); } diff --git a/core/object.h b/core/object.h index 8dc3426d1d..88a98dacbe 100644 --- a/core/object.h +++ b/core/object.h @@ -31,13 +31,13 @@ #ifndef OBJECT_H #define OBJECT_H -#include "hash_map.h" -#include "list.h" -#include "map.h" -#include "os/rw_lock.h" -#include "set.h" -#include "variant.h" -#include "vmap.h" +#include "core/hash_map.h" +#include "core/list.h" +#include "core/map.h" +#include "core/os/rw_lock.h" +#include "core/set.h" +#include "core/variant.h" +#include "core/vmap.h" #define VARIANT_ARG_LIST const Variant &p_arg1 = Variant(), const Variant &p_arg2 = Variant(), const Variant &p_arg3 = Variant(), const Variant &p_arg4 = Variant(), const Variant &p_arg5 = Variant() #define VARIANT_ARG_PASS p_arg1, p_arg2, p_arg3, p_arg4, p_arg5 @@ -71,6 +71,7 @@ enum PropertyHint { PROPERTY_HINT_GLOBAL_DIR, ///< a directory path must be passed PROPERTY_HINT_RESOURCE_TYPE, ///< a resource object type PROPERTY_HINT_MULTILINE_TEXT, ///< used for string properties that can contain multiple lines + PROPERTY_HINT_PLACEHOLDER_TEXT, ///< used to set a placeholder text for string properties PROPERTY_HINT_COLOR_NO_ALPHA, ///< used for ignoring alpha component when editing a color PROPERTY_HINT_IMAGE_COMPRESS_LOSSY, PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS, @@ -102,8 +103,9 @@ enum PropertyUsageFlags { PROPERTY_USAGE_INTERNATIONALIZED = 64, //hint for internationalized strings PROPERTY_USAGE_GROUP = 128, //used for grouping props in the editor PROPERTY_USAGE_CATEGORY = 256, - PROPERTY_USAGE_STORE_IF_NONZERO = 512, //only store if nonzero - PROPERTY_USAGE_STORE_IF_NONONE = 1024, //only store if false + //those below are deprecated thanks to ClassDB's now class value cache + //PROPERTY_USAGE_STORE_IF_NONZERO = 512, //only store if nonzero + //PROPERTY_USAGE_STORE_IF_NONONE = 1024, //only store if false PROPERTY_USAGE_NO_INSTANCE_STATE = 2048, PROPERTY_USAGE_RESTART_IF_CHANGED = 4096, PROPERTY_USAGE_SCRIPT_VARIABLE = 8192, @@ -115,6 +117,7 @@ enum PropertyUsageFlags { PROPERTY_USAGE_NIL_IS_VARIANT = 1 << 19, PROPERTY_USAGE_INTERNAL = 1 << 20, PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE = 1 << 21, // If the object is duplicated also this property will be duplicated + PROPERTY_USAGE_HIGH_END_GFX = 1 << 22, PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK, PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK | PROPERTY_USAGE_INTERNATIONALIZED, @@ -124,10 +127,6 @@ enum PropertyUsageFlags { #define ADD_SIGNAL(m_signal) ClassDB::add_signal(get_class_static(), m_signal) #define ADD_PROPERTY(m_property, m_setter, m_getter) ClassDB::add_property(get_class_static(), m_property, _scs_create(m_setter), _scs_create(m_getter)) #define ADD_PROPERTYI(m_property, m_setter, m_getter, m_index) ClassDB::add_property(get_class_static(), m_property, _scs_create(m_setter), _scs_create(m_getter), m_index) -#define ADD_PROPERTYNZ(m_property, m_setter, m_getter) ClassDB::add_property(get_class_static(), (m_property).added_usage(PROPERTY_USAGE_STORE_IF_NONZERO), _scs_create(m_setter), _scs_create(m_getter)) -#define ADD_PROPERTYINZ(m_property, m_setter, m_getter, m_index) ClassDB::add_property(get_class_static(), (m_property).added_usage(PROPERTY_USAGE_STORE_IF_NONZERO), _scs_create(m_setter), _scs_create(m_getter), m_index) -#define ADD_PROPERTYNO(m_property, m_setter, m_getter) ClassDB::add_property(get_class_static(), (m_property).added_usage(PROPERTY_USAGE_STORE_IF_NONONE), _scs_create(m_setter), _scs_create(m_getter)) -#define ADD_PROPERTYINO(m_property, m_setter, m_getter, m_index) ClassDB::add_property(get_class_static(), (m_property).added_usage(PROPERTY_USAGE_STORE_IF_NONONE), _scs_create(m_setter), _scs_create(m_getter), m_index) #define ADD_GROUP(m_name, m_prefix) ClassDB::add_property_group(get_class_static(), m_name, m_prefix) struct PropertyInfo { @@ -186,11 +185,11 @@ Array convert_property_list(const List<PropertyInfo> *p_list); struct MethodInfo { String name; - List<PropertyInfo> arguments; - Vector<Variant> default_arguments; PropertyInfo return_val; uint32_t flags; int id; + List<PropertyInfo> arguments; + Vector<Variant> default_arguments; inline bool operator==(const MethodInfo &p_method) const { return id == p_method.id; } inline bool operator<(const MethodInfo &p_method) const { return id == p_method.id ? (name < p_method.name) : (id < p_method.id); } @@ -316,7 +315,7 @@ protected: virtual void _initialize_classv() { \ initialize_class(); \ } \ - _FORCE_INLINE_ bool (Object::*(_get_get() const))(const StringName &p_name, Variant &) const { \ + _FORCE_INLINE_ bool (Object::*_get_get() const)(const StringName &p_name, Variant &) const { \ return (bool (Object::*)(const StringName &, Variant &) const) & m_class::_get; \ } \ virtual bool _getv(const StringName &p_name, Variant &r_ret) const { \ @@ -326,7 +325,7 @@ protected: } \ return m_inherits::_getv(p_name, r_ret); \ } \ - _FORCE_INLINE_ bool (Object::*(_get_set() const))(const StringName &p_name, const Variant &p_property) { \ + _FORCE_INLINE_ bool (Object::*_get_set() const)(const StringName &p_name, const Variant &p_property) { \ return (bool (Object::*)(const StringName &, const Variant &)) & m_class::_set; \ } \ virtual bool _setv(const StringName &p_name, const Variant &p_property) { \ @@ -336,7 +335,7 @@ protected: } \ return false; \ } \ - _FORCE_INLINE_ void (Object::*(_get_get_property_list() const))(List<PropertyInfo> * p_list) const { \ + _FORCE_INLINE_ void (Object::*_get_get_property_list() const)(List<PropertyInfo> * p_list) const { \ return (void (Object::*)(List<PropertyInfo> *) const) & m_class::_get_property_list; \ } \ virtual void _get_property_listv(List<PropertyInfo> *p_list, bool p_reversed) const { \ @@ -355,7 +354,7 @@ protected: m_inherits::_get_property_listv(p_list, p_reversed); \ } \ } \ - _FORCE_INLINE_ void (Object::*(_get_notification() const))(int) { \ + _FORCE_INLINE_ void (Object::*_get_notification() const)(int) { \ return (void (Object::*)(int)) & m_class::_notification; \ } \ virtual void _notificationv(int p_notification, bool p_reversed) { \ @@ -391,7 +390,8 @@ public: CONNECT_DEFERRED = 1, CONNECT_PERSIST = 2, // hint for scene to save this connection - CONNECT_ONESHOT = 4 + CONNECT_ONESHOT = 4, + CONNECT_REFERENCE_COUNTED = 8, }; struct Connection { @@ -419,7 +419,7 @@ private: }; #ifdef DEBUG_ENABLED - friend class _ObjectDebugLock; + friend struct _ObjectDebugLock; #endif friend bool predelete_handler(Object *); friend void postinitialize_handler(Object *); @@ -442,8 +442,10 @@ private: struct Slot { + int reference_count; Connection conn; List<Connection>::Element *cE; + Slot() { reference_count = 0; } }; MethodInfo user; @@ -486,10 +488,12 @@ private: void _set_indexed_bind(const NodePath &p_name, const Variant &p_value); Variant _get_indexed_bind(const NodePath &p_name) const; - void *_script_instance_bindings[MAX_SCRIPT_INSTANCE_BINDINGS]; - void property_list_changed_notify(); + friend class Reference; + uint32_t instance_binding_count; + void *_script_instance_bindings[MAX_SCRIPT_INSTANCE_BINDINGS]; + protected: virtual void _initialize_classv() { initialize_class(); } virtual bool _setv(const StringName &p_name, const Variant &p_property) { return false; }; @@ -507,16 +511,16 @@ protected: _FORCE_INLINE_ static void (*_get_bind_methods())() { return &Object::_bind_methods; } - _FORCE_INLINE_ bool (Object::*(_get_get() const))(const StringName &p_name, Variant &r_ret) const { + _FORCE_INLINE_ bool (Object::*_get_get() const)(const StringName &p_name, Variant &r_ret) const { return &Object::_get; } - _FORCE_INLINE_ bool (Object::*(_get_set() const))(const StringName &p_name, const Variant &p_property) { + _FORCE_INLINE_ bool (Object::*_get_set() const)(const StringName &p_name, const Variant &p_property) { return &Object::_set; } - _FORCE_INLINE_ void (Object::*(_get_get_property_list() const))(List<PropertyInfo> *p_list) const { + _FORCE_INLINE_ void (Object::*_get_get_property_list() const)(List<PropertyInfo> *p_list) const { return &Object::_get_property_list; } - _FORCE_INLINE_ void (Object::*(_get_notification() const))(int) { + _FORCE_INLINE_ void (Object::*_get_notification() const)(int) { return &Object::_notification; } static void get_valid_parents_static(List<String> *p_parents); @@ -547,6 +551,8 @@ protected: friend class ClassDB; virtual void _validate_property(PropertyInfo &property) const; + void _disconnect(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method, bool p_force = false); + public: //should be protected, but bug in clang++ static void initialize_class(); _FORCE_INLINE_ static void register_custom_data_to_otdb(){}; @@ -692,6 +698,7 @@ public: bool is_connected(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method) const; void call_deferred(const StringName &p_method, VARIANT_ARG_LIST); + void set_deferred(const StringName &p_property, const Variant &p_value); void set_block_signals(bool p_block); bool is_blocking_signals() const; @@ -714,6 +721,9 @@ public: #ifdef TOOLS_ENABLED void editor_set_section_unfold(const String &p_section, bool p_unfolded); bool editor_is_section_unfolded(const String &p_section); + const Set<String> &editor_get_section_folding() const { return editor_section_folding; } + void editor_clear_section_folding() { editor_section_folding.clear(); } + #endif //used by script languages to store binding data @@ -771,6 +781,6 @@ public: }; //needed by macros -#include "class_db.h" +#include "core/class_db.h" #endif diff --git a/core/ordered_hash_map.h b/core/ordered_hash_map.h index 93ce9a90a7..092bf314d0 100644 --- a/core/ordered_hash_map.h +++ b/core/ordered_hash_map.h @@ -31,9 +31,9 @@ #ifndef ORDERED_HASH_MAP_H #define ORDERED_HASH_MAP_H -#include "hash_map.h" -#include "list.h" -#include "pair.h" +#include "core/hash_map.h" +#include "core/list.h" +#include "core/pair.h" /** * A hash map which allows to iterate elements in insertion order. diff --git a/core/os/SCsub b/core/os/SCsub index 4efc902717..1c5f954470 100644 --- a/core/os/SCsub +++ b/core/os/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.core_sources, "*.cpp") - -Export('env') diff --git a/core/os/copymem.h b/core/os/copymem.h index 87d77bd426..999c770e85 100644 --- a/core/os/copymem.h +++ b/core/os/copymem.h @@ -31,7 +31,7 @@ #ifndef COPYMEM_H #define COPYMEM_H -#include "typedefs.h" +#include "core/typedefs.h" #ifdef PLATFORM_COPYMEM diff --git a/core/os/dir_access.cpp b/core/os/dir_access.cpp index 330a9153ef..ce70b859eb 100644 --- a/core/os/dir_access.cpp +++ b/core/os/dir_access.cpp @@ -29,10 +29,11 @@ /*************************************************************************/ #include "dir_access.h" -#include "os/file_access.h" -#include "os/memory.h" -#include "os/os.h" -#include "project_settings.h" + +#include "core/os/file_access.h" +#include "core/os/memory.h" +#include "core/os/os.h" +#include "core/project_settings.h" String DirAccess::_get_root_path() const { @@ -98,22 +99,18 @@ static Error _erase_recursive(DirAccess *da) { err = _erase_recursive(da); if (err) { - print_line("err recurso " + E->get()); da->change_dir(".."); return err; } err = da->change_dir(".."); if (err) { - print_line("no go back " + E->get()); return err; } err = da->remove(da->get_current_dir().plus_file(E->get())); if (err) { - print_line("no remove dir" + E->get()); return err; } } else { - print_line("no change to " + E->get()); return err; } } @@ -122,8 +119,6 @@ static Error _erase_recursive(DirAccess *da) { Error err = da->remove(da->get_current_dir().plus_file(E->get())); if (err) { - - print_line("no remove file" + E->get()); return err; } } @@ -232,6 +227,7 @@ String DirAccess::fix_path(String p_path) const { return p_path; } break; + case ACCESS_MAX: break; // Can't happen, but silences warning } return p_path; @@ -435,8 +431,12 @@ Error DirAccess::copy_dir(String p_from, String p_to, int p_chmod_flags) { ERR_FAIL_COND_V(err, err); } + if (!p_to.ends_with("/")) { + p_to = p_to + "/"; + } + DirChanger dir_changer(this, p_from); - Error err = _copy_dir(target_da, p_to + "/", p_chmod_flags); + Error err = _copy_dir(target_da, p_to, p_chmod_flags); memdelete(target_da); return err; diff --git a/core/os/dir_access.h b/core/os/dir_access.h index 4df0618021..773f0bcd41 100644 --- a/core/os/dir_access.h +++ b/core/os/dir_access.h @@ -31,14 +31,14 @@ #ifndef DIR_ACCESS_H #define DIR_ACCESS_H -#include "typedefs.h" -#include "ustring.h" +#include "core/typedefs.h" +#include "core/ustring.h" /** @author Juan Linietsky <reduzio@gmail.com> */ -//@ TOOD, excellent candidate for THREAD_SAFE MACRO, should go through all these and add THREAD_SAFE where it applies +//@ TODO, excellent candidate for THREAD_SAFE MACRO, should go through all these and add THREAD_SAFE where it applies class DirAccess { public: enum AccessType { diff --git a/core/os/file_access.cpp b/core/os/file_access.cpp index 3eac4428da..679b1c9054 100644 --- a/core/os/file_access.cpp +++ b/core/os/file_access.cpp @@ -32,8 +32,8 @@ #include "core/io/file_access_pack.h" #include "core/io/marshalls.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include "thirdparty/misc/md5.h" #include "thirdparty/misc/sha256.h" @@ -46,7 +46,6 @@ bool FileAccess::backup_save = false; FileAccess *FileAccess::create(AccessType p_access) { - ERR_FAIL_COND_V(!create_func, 0); ERR_FAIL_INDEX_V(p_access, ACCESS_MAX, 0); FileAccess *ret = create_func[p_access](); @@ -166,6 +165,7 @@ String FileAccess::fix_path(const String &p_path) const { return r_path; } break; + case ACCESS_MAX: break; // Can't happen, but silences warning } return r_path; @@ -270,7 +270,6 @@ String FileAccess::get_token() const { c = get_8(); } - token += '0'; return String::utf8(token.get_data()); } @@ -347,9 +346,9 @@ String FileAccess::get_line() const { return String::utf8(line.get_data()); } -Vector<String> FileAccess::get_csv_line(String delim) const { +Vector<String> FileAccess::get_csv_line(const String &p_delim) const { - ERR_FAIL_COND_V(delim.length() != 1, Vector<String>()); + ERR_FAIL_COND_V(p_delim.length() != 1, Vector<String>()); String l; int qc = 0; @@ -377,7 +376,7 @@ Vector<String> FileAccess::get_csv_line(String delim) const { CharType c = l[i]; CharType s[2] = { 0, 0 }; - if (!in_quote && c == delim[0]) { + if (!in_quote && c == p_delim[0]) { strings.push_back(current); current = String(); } else if (c == '"') { @@ -526,6 +525,28 @@ void FileAccess::store_line(const String &p_line) { store_8('\n'); } +void FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_delim) { + + ERR_FAIL_COND(p_delim.length() != 1); + + String line = ""; + int size = p_values.size(); + for (int i = 0; i < size; ++i) { + String value = p_values[i]; + + if (value.find("\"") != -1 || value.find(p_delim) != -1 || value.find("\n") != -1) { + value = "\"" + value.replace("\"", "\"\"") + "\""; + } + if (i < size - 1) { + value += p_delim; + } + + line += value; + } + + store_line(line); +} + void FileAccess::store_buffer(const uint8_t *p_src, int p_length) { for (int i = 0; i < p_length; i++) diff --git a/core/os/file_access.h b/core/os/file_access.h index c4635fdfbb..f1f3005dd9 100644 --- a/core/os/file_access.h +++ b/core/os/file_access.h @@ -31,10 +31,11 @@ #ifndef FILE_ACCESS_H #define FILE_ACCESS_H -#include "math_defs.h" -#include "os/memory.h" -#include "typedefs.h" -#include "ustring.h" +#include "core/math/math_defs.h" +#include "core/os/memory.h" +#include "core/typedefs.h" +#include "core/ustring.h" + /** * Multi-Platform abstraction for accessing to files. */ @@ -111,7 +112,7 @@ public: virtual int get_buffer(uint8_t *p_dst, int p_length) const; ///< get an array of bytes virtual String get_line() const; virtual String get_token() const; - virtual Vector<String> get_csv_line(String delim = ",") const; + virtual Vector<String> get_csv_line(const String &p_delim = ",") const; /**< use this for files WRITTEN in _big_ endian machines (ie, amiga/mac) * It's not about the current CPU type but file formats. @@ -135,6 +136,7 @@ public: virtual void store_string(const String &p_string); virtual void store_line(const String &p_line); + virtual void store_csv_line(const Vector<String> &p_values, const String &p_delim = ","); virtual void store_pascal_string(const String &p_string); virtual String get_pascal_string(); diff --git a/core/os/input.cpp b/core/os/input.cpp index a5b0f91e63..3b895b16b4 100644 --- a/core/os/input.cpp +++ b/core/os/input.cpp @@ -29,9 +29,11 @@ /*************************************************************************/ #include "input.h" -#include "input_map.h" -#include "os/os.h" -#include "project_settings.h" + +#include "core/input_map.h" +#include "core/os/os.h" +#include "core/project_settings.h" + Input *Input::singleton = NULL; Input *Input::get_singleton() { @@ -40,7 +42,7 @@ Input *Input::get_singleton() { } void Input::set_mouse_mode(MouseMode p_mode) { - ERR_FAIL_INDEX(p_mode, 4); + ERR_FAIL_INDEX((int)p_mode, 4); OS::get_singleton()->set_mouse_mode((OS::MouseMode)p_mode); } @@ -84,7 +86,7 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("set_mouse_mode", "mode"), &Input::set_mouse_mode); ClassDB::bind_method(D_METHOD("get_mouse_mode"), &Input::get_mouse_mode); ClassDB::bind_method(D_METHOD("warp_mouse_position", "to"), &Input::warp_mouse_position); - ClassDB::bind_method(D_METHOD("action_press", "action"), &Input::action_press); + ClassDB::bind_method(D_METHOD("action_press", "action", "strength"), &Input::action_press, DEFVAL(1.f)); ClassDB::bind_method(D_METHOD("action_release", "action"), &Input::action_release); ClassDB::bind_method(D_METHOD("set_default_cursor_shape", "shape"), &Input::set_default_cursor_shape, DEFVAL(CURSOR_ARROW)); ClassDB::bind_method(D_METHOD("set_custom_mouse_cursor", "image", "shape", "hotspot"), &Input::set_custom_mouse_cursor, DEFVAL(CURSOR_ARROW), DEFVAL(Vector2())); diff --git a/core/os/input.h b/core/os/input.h index 001871c5dc..dc2c213db2 100644 --- a/core/os/input.h +++ b/core/os/input.h @@ -31,9 +31,9 @@ #ifndef INPUT_H #define INPUT_H -#include "object.h" -#include "os/main_loop.h" -#include "os/thread_safe.h" +#include "core/object.h" +#include "core/os/main_loop.h" +#include "core/os/thread_safe.h" class Input : public Object { @@ -113,7 +113,7 @@ public: virtual Vector3 get_magnetometer() const = 0; virtual Vector3 get_gyroscope() const = 0; - virtual void action_press(const StringName &p_action) = 0; + virtual void action_press(const StringName &p_action, float p_strength = 1.f) = 0; virtual void action_release(const StringName &p_action) = 0; void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const; diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp index 12c6ef7d3b..5bbdd7efb2 100644 --- a/core/os/input_event.cpp +++ b/core/os/input_event.cpp @@ -30,8 +30,8 @@ #include "input_event.h" -#include "input_map.h" -#include "os/keyboard.h" +#include "core/input_map.h" +#include "core/os/keyboard.h" void InputEvent::set_device(int p_device) { device = p_device; @@ -962,6 +962,22 @@ bool InputEventAction::is_action(const StringName &p_action) const { return action == p_action; } +bool InputEventAction::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const { + + Ref<InputEventAction> act = p_event; + if (act.is_null()) + return false; + + bool match = action == act->action; + if (match) { + if (p_pressed != NULL) + *p_pressed = act->pressed; + if (p_strength != NULL) + *p_strength = (*p_pressed) ? 1.0f : 0.0f; + } + return match; +} + String InputEventAction::as_text() const { return "InputEventAction : action=" + action + ", pressed=(" + (pressed ? "true" : "false"); diff --git a/core/os/input_event.h b/core/os/input_event.h index 07df81488b..789d19c5b2 100644 --- a/core/os/input_event.h +++ b/core/os/input_event.h @@ -31,11 +31,12 @@ #ifndef INPUT_EVENT_H #define INPUT_EVENT_H -#include "math_2d.h" -#include "os/copymem.h" -#include "resource.h" -#include "typedefs.h" -#include "ustring.h" +#include "core/math/transform_2d.h" +#include "core/os/copymem.h" +#include "core/resource.h" +#include "core/typedefs.h" +#include "core/ustring.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -480,6 +481,8 @@ public: virtual bool is_action(const StringName &p_action) const; + virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const; + virtual bool shortcut_match(const Ref<InputEvent> &p_event) const; virtual bool is_action_type() const { return true; } virtual String as_text() const; diff --git a/core/os/keyboard.cpp b/core/os/keyboard.cpp index 9dfc91e308..abc579c58d 100644 --- a/core/os/keyboard.cpp +++ b/core/os/keyboard.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "keyboard.h" -#include "os/os.h" + +#include "core/os/os.h" struct _KeyCodeText { int code; diff --git a/core/os/keyboard.h b/core/os/keyboard.h index a0e6f8b2ef..17bf5eaa48 100644 --- a/core/os/keyboard.h +++ b/core/os/keyboard.h @@ -31,13 +31,10 @@ #ifndef KEYBOARD_H #define KEYBOARD_H -#include "ustring.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ +#include "core/ustring.h" /** -@author Juan Linietsky <reduzio@gmail.com> + @author Juan Linietsky <reduzio@gmail.com> */ /* diff --git a/core/os/main_loop.cpp b/core/os/main_loop.cpp index c51801e3e2..0945cdd512 100644 --- a/core/os/main_loop.cpp +++ b/core/os/main_loop.cpp @@ -29,18 +29,19 @@ /*************************************************************************/ #include "main_loop.h" -#include "script_language.h" + +#include "core/script_language.h" void MainLoop::_bind_methods() { - ClassDB::bind_method(D_METHOD("input_event", "ev"), &MainLoop::input_event); + ClassDB::bind_method(D_METHOD("input_event", "event"), &MainLoop::input_event); ClassDB::bind_method(D_METHOD("input_text", "text"), &MainLoop::input_text); ClassDB::bind_method(D_METHOD("init"), &MainLoop::init); ClassDB::bind_method(D_METHOD("iteration", "delta"), &MainLoop::iteration); ClassDB::bind_method(D_METHOD("idle", "delta"), &MainLoop::idle); ClassDB::bind_method(D_METHOD("finish"), &MainLoop::finish); - BIND_VMETHOD(MethodInfo("_input_event", PropertyInfo(Variant::OBJECT, "ev", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); + BIND_VMETHOD(MethodInfo("_input_event", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); BIND_VMETHOD(MethodInfo("_input_text", PropertyInfo(Variant::STRING, "text"))); BIND_VMETHOD(MethodInfo("_initialize")); BIND_VMETHOD(MethodInfo("_iteration", PropertyInfo(Variant::REAL, "delta"))); diff --git a/core/os/main_loop.h b/core/os/main_loop.h index f96e46141e..43f74302a8 100644 --- a/core/os/main_loop.h +++ b/core/os/main_loop.h @@ -31,12 +31,14 @@ #ifndef MAIN_LOOP_H #define MAIN_LOOP_H -#include "os/input_event.h" -#include "reference.h" -#include "script_language.h" +#include "core/os/input_event.h" +#include "core/reference.h" +#include "core/script_language.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ + class MainLoop : public Object { GDCLASS(MainLoop, Object); diff --git a/core/os/memory.cpp b/core/os/memory.cpp index 3eab4343a9..f25e40ef78 100644 --- a/core/os/memory.cpp +++ b/core/os/memory.cpp @@ -29,9 +29,11 @@ /*************************************************************************/ #include "memory.h" -#include "copymem.h" + +#include "core/error_macros.h" +#include "core/os/copymem.h" #include "core/safe_refcount.h" -#include "error_macros.h" + #include <stdio.h> #include <stdlib.h> @@ -170,9 +172,9 @@ void Memory::free_static(void *p_ptr, bool p_pad_align) { if (prepad) { mem -= PAD_ALIGN; - uint64_t *s = (uint64_t *)mem; #ifdef DEBUG_ENABLED + uint64_t *s = (uint64_t *)mem; atomic_sub(&mem_usage, *s); #endif diff --git a/core/os/memory.h b/core/os/memory.h index f5c6c0b38a..3501c3f14e 100644 --- a/core/os/memory.h +++ b/core/os/memory.h @@ -31,7 +31,8 @@ #ifndef MEMORY_H #define MEMORY_H -#include "safe_refcount.h" +#include "core/safe_refcount.h" + #include <stddef.h> /** diff --git a/core/os/midi_driver.cpp b/core/os/midi_driver.cpp index 7b4f84473c..2b20a708ed 100644 --- a/core/os/midi_driver.cpp +++ b/core/os/midi_driver.cpp @@ -30,8 +30,8 @@ #include "midi_driver.h" +#include "core/os/os.h" #include "main/input_default.h" -#include "os/os.h" MIDIDriver *MIDIDriver::singleton = NULL; MIDIDriver *MIDIDriver::get_singleton() { diff --git a/core/os/midi_driver.h b/core/os/midi_driver.h index 1a3a67a411..ceb4e71d66 100644 --- a/core/os/midi_driver.h +++ b/core/os/midi_driver.h @@ -31,8 +31,9 @@ #ifndef MIDI_DRIVER_H #define MIDI_DRIVER_H +#include "core/typedefs.h" #include "core/variant.h" -#include "typedefs.h" + /** * Multi-Platform abstraction for accessing to MIDI. */ diff --git a/core/os/mutex.cpp b/core/os/mutex.cpp index 7c4ea2323c..0c01b06bd6 100644 --- a/core/os/mutex.cpp +++ b/core/os/mutex.cpp @@ -29,7 +29,9 @@ /*************************************************************************/ #include "mutex.h" -#include "error_macros.h" + +#include "core/error_macros.h" + #include <stddef.h> Mutex *(*Mutex::create_func)(bool) = 0; diff --git a/core/os/mutex.h b/core/os/mutex.h index 9debe7f41b..788cc00397 100644 --- a/core/os/mutex.h +++ b/core/os/mutex.h @@ -31,7 +31,7 @@ #ifndef MUTEX_H #define MUTEX_H -#include "error_list.h" +#include "core/error_list.h" /** * @class Mutex diff --git a/core/os/os.cpp b/core/os/os.cpp index 97dae05919..7547b6a042 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -30,13 +30,13 @@ #include "os.h" -#include "dir_access.h" -#include "input.h" -#include "os/file_access.h" -#include "os/midi_driver.h" -#include "project_settings.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" +#include "core/os/input.h" +#include "core/os/midi_driver.h" +#include "core/project_settings.h" +#include "core/version_generated.gen.h" #include "servers/audio_server.h" -#include "version_generated.gen.h" #include <stdarg.h> @@ -632,10 +632,13 @@ void OS::center_window() { if (is_window_fullscreen()) return; + Point2 sp = get_screen_position(get_current_screen()); Size2 scr = get_screen_size(get_current_screen()); Size2 wnd = get_real_window_size(); - int x = scr.width / 2 - wnd.width / 2; - int y = scr.height / 2 - wnd.height / 2; + + int x = sp.width + (scr.width - wnd.width) / 2; + int y = sp.height + (scr.height - wnd.height) / 2; + set_window_position(Vector2(x, y)); } @@ -689,6 +692,18 @@ PoolStringArray OS::get_connected_midi_inputs() { return list; } +void OS::open_midi_inputs() { + + if (MIDIDriver::get_singleton()) + MIDIDriver::get_singleton()->open(); +} + +void OS::close_midi_inputs() { + + if (MIDIDriver::get_singleton()) + MIDIDriver::get_singleton()->close(); +} + OS::OS() { void *volatile stack_bottom; diff --git a/core/os/os.h b/core/os/os.h index dd783408e8..53a5ebde01 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -31,13 +31,14 @@ #ifndef OS_H #define OS_H -#include "engine.h" -#include "image.h" -#include "io/logger.h" -#include "list.h" -#include "os/main_loop.h" -#include "ustring.h" -#include "vector.h" +#include "core/engine.h" +#include "core/image.h" +#include "core/io/logger.h" +#include "core/list.h" +#include "core/os/main_loop.h" +#include "core/ustring.h" +#include "core/vector.h" + #include <stdarg.h> /** @@ -128,7 +129,7 @@ protected: RenderThreadMode _render_thread_mode; - // functions used by main to initialize/deintialize the OS + // functions used by main to initialize/deinitialize the OS void add_logger(Logger *p_logger); virtual void initialize_core() = 0; @@ -190,6 +191,8 @@ public: virtual const char *get_audio_driver_name(int p_driver) const; virtual PoolStringArray get_connected_midi_inputs(); + virtual void open_midi_inputs(); + virtual void close_midi_inputs(); virtual int get_screen_count() const { return 1; } virtual int get_current_screen() const { return 0; } @@ -477,6 +480,7 @@ public: enum EngineContext { CONTEXT_EDITOR, CONTEXT_PROJECTMAN, + CONTEXT_ENGINE, }; virtual void set_context(int p_context); diff --git a/core/os/rw_lock.cpp b/core/os/rw_lock.cpp index 35489490ed..5e51a1dbce 100644 --- a/core/os/rw_lock.cpp +++ b/core/os/rw_lock.cpp @@ -30,7 +30,7 @@ #include "rw_lock.h" -#include "error_macros.h" +#include "core/error_macros.h" #include <stddef.h> diff --git a/core/os/rw_lock.h b/core/os/rw_lock.h index 9053794c83..4333d9a016 100644 --- a/core/os/rw_lock.h +++ b/core/os/rw_lock.h @@ -31,7 +31,7 @@ #ifndef RWLOCK_H #define RWLOCK_H -#include "error_list.h" +#include "core/error_list.h" class RWLock { protected: @@ -56,8 +56,8 @@ class RWLockRead { RWLock *lock; public: - RWLockRead(RWLock *p_lock) { - lock = p_lock; + RWLockRead(const RWLock *p_lock) { + lock = const_cast<RWLock *>(p_lock); if (lock) lock->read_lock(); } ~RWLockRead() { diff --git a/core/os/semaphore.cpp b/core/os/semaphore.cpp index 0377aeeb29..448d17dd14 100644 --- a/core/os/semaphore.cpp +++ b/core/os/semaphore.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "semaphore.h" -#include "error_macros.h" + +#include "core/error_macros.h" Semaphore *(*Semaphore::create_func)() = 0; diff --git a/core/os/semaphore.h b/core/os/semaphore.h index f3021bf74c..6cec06ea28 100644 --- a/core/os/semaphore.h +++ b/core/os/semaphore.h @@ -31,11 +31,12 @@ #ifndef SEMAPHORE_H #define SEMAPHORE_H -#include "error_list.h" +#include "core/error_list.h" /** @author Juan Linietsky <reduzio@gmail.com> */ + class Semaphore { protected: static Semaphore *(*create_func)(); diff --git a/core/os/shell.h b/core/os/shell.h index d3d92028ea..146c35cbc8 100644 --- a/core/os/shell.h +++ b/core/os/shell.h @@ -31,8 +31,8 @@ #ifndef SHELL_H #define SHELL_H -#include "typedefs.h" -#include "ustring.h" +#include "core/typedefs.h" +#include "core/ustring.h" /** @author Juan Linietsky <reduzio@gmail.com> diff --git a/core/os/thread.h b/core/os/thread.h index c2947bccab..97e97652a5 100644 --- a/core/os/thread.h +++ b/core/os/thread.h @@ -31,13 +31,13 @@ #ifndef THREAD_H #define THREAD_H -#include "typedefs.h" +#include "core/typedefs.h" +#include "core/ustring.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ -#include "ustring.h" - typedef void (*ThreadCreateCallback)(void *p_userdata); class Thread { diff --git a/core/os/thread_dummy.cpp b/core/os/thread_dummy.cpp index b6371235c4..9e0adf9994 100644 --- a/core/os/thread_dummy.cpp +++ b/core/os/thread_dummy.cpp @@ -30,7 +30,7 @@ #include "thread_dummy.h" -#include "memory.h" +#include "core/os/memory.h" Thread *ThreadDummy::create(ThreadCreateCallback p_callback, void *p_user, const Thread::Settings &p_settings) { return memnew(ThreadDummy); diff --git a/core/os/thread_dummy.h b/core/os/thread_dummy.h index 74957b95fe..6f46a4a20c 100644 --- a/core/os/thread_dummy.h +++ b/core/os/thread_dummy.h @@ -31,10 +31,10 @@ #ifndef THREAD_DUMMY_H #define THREAD_DUMMY_H -#include "mutex.h" -#include "rw_lock.h" -#include "semaphore.h" -#include "thread.h" +#include "core/os/mutex.h" +#include "core/os/rw_lock.h" +#include "core/os/semaphore.h" +#include "core/os/thread.h" class ThreadDummy : public Thread { diff --git a/core/os/thread_safe.cpp b/core/os/thread_safe.cpp index acb37df02b..3fe039e1b6 100644 --- a/core/os/thread_safe.cpp +++ b/core/os/thread_safe.cpp @@ -29,8 +29,9 @@ /*************************************************************************/ #include "thread_safe.h" -#include "error_macros.h" -#include "os/memory.h" + +#include "core/error_macros.h" +#include "core/os/memory.h" ThreadSafe::ThreadSafe() { diff --git a/core/os/thread_safe.h b/core/os/thread_safe.h index f0876f38a1..a17ceeed1d 100644 --- a/core/os/thread_safe.h +++ b/core/os/thread_safe.h @@ -31,7 +31,7 @@ #ifndef THREAD_SAFE_H #define THREAD_SAFE_H -#include "os/mutex.h" +#include "core/os/mutex.h" class ThreadSafe { diff --git a/core/os/threaded_array_processor.h b/core/os/threaded_array_processor.h index 3ff7db2a44..89c3b7cec3 100644 --- a/core/os/threaded_array_processor.h +++ b/core/os/threaded_array_processor.h @@ -31,11 +31,11 @@ #ifndef THREADED_ARRAY_PROCESSOR_H #define THREADED_ARRAY_PROCESSOR_H -#include "os/mutex.h" -#include "os/os.h" -#include "os/thread.h" -#include "safe_refcount.h" -#include "thread_safe.h" +#include "core/os/mutex.h" +#include "core/os/os.h" +#include "core/os/thread.h" +#include "core/os/thread_safe.h" +#include "core/safe_refcount.h" template <class C, class U> struct ThreadArrayProcessData { diff --git a/core/packed_data_container.cpp b/core/packed_data_container.cpp index 45e060fa4a..e4ca8fafa5 100644 --- a/core/packed_data_container.cpp +++ b/core/packed_data_container.cpp @@ -30,8 +30,8 @@ #include "packed_data_container.h" -#include "core_string_names.h" -#include "io/marshalls.h" +#include "core/core_string_names.h" +#include "core/io/marshalls.h" Variant PackedDataContainer::getvar(const Variant &p_key, bool *r_valid) const { diff --git a/core/packed_data_container.h b/core/packed_data_container.h index fe36417000..0e1dff1f70 100644 --- a/core/packed_data_container.h +++ b/core/packed_data_container.h @@ -31,7 +31,7 @@ #ifndef PACKED_DATA_CONTAINER_H #define PACKED_DATA_CONTAINER_H -#include "resource.h" +#include "core/resource.h" class PackedDataContainer : public Resource { diff --git a/core/pool_allocator.cpp b/core/pool_allocator.cpp index 8952314212..6997c993f4 100644 --- a/core/pool_allocator.cpp +++ b/core/pool_allocator.cpp @@ -30,11 +30,11 @@ #include "pool_allocator.h" +#include "core/error_macros.h" +#include "core/os/copymem.h" +#include "core/os/memory.h" #include "core/os/os.h" -#include "error_macros.h" -#include "os/copymem.h" -#include "os/memory.h" -#include "print_string.h" +#include "core/print_string.h" #include <assert.h> diff --git a/core/pool_allocator.h b/core/pool_allocator.h index d9731aa3eb..12e2b597db 100644 --- a/core/pool_allocator.h +++ b/core/pool_allocator.h @@ -31,7 +31,7 @@ #ifndef POOL_ALLOCATOR_H #define POOL_ALLOCATOR_H -#include "typedefs.h" +#include "core/typedefs.h" /** @author Juan Linietsky <reduzio@gmail.com> diff --git a/core/print_string.cpp b/core/print_string.cpp index 0355154488..f53d50e078 100644 --- a/core/print_string.cpp +++ b/core/print_string.cpp @@ -30,7 +30,7 @@ #include "print_string.h" -#include "os/os.h" +#include "core/os/os.h" #include <stdio.h> @@ -107,3 +107,10 @@ void print_error(String p_string) { _global_unlock(); } + +void print_verbose(String p_string) { + + if (OS::get_singleton()->is_stdout_verbose()) { + print_line(p_string); + } +} diff --git a/core/print_string.h b/core/print_string.h index 3465888d4c..8f4fbb1f7b 100644 --- a/core/print_string.h +++ b/core/print_string.h @@ -31,7 +31,7 @@ #ifndef PRINT_STRING_H #define PRINT_STRING_H -#include "ustring.h" +#include "core/ustring.h" extern void (*_print_func)(String); @@ -58,5 +58,6 @@ extern bool _print_line_enabled; extern bool _print_error_enabled; extern void print_line(String p_string); extern void print_error(String p_string); +extern void print_verbose(String p_string); #endif diff --git a/core/project_settings.cpp b/core/project_settings.cpp index 146b4870e8..407bb78375 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -30,16 +30,17 @@ #include "project_settings.h" -#include "bind/core_bind.h" -#include "core_string_names.h" -#include "io/file_access_network.h" -#include "io/file_access_pack.h" -#include "io/marshalls.h" -#include "os/dir_access.h" -#include "os/file_access.h" -#include "os/keyboard.h" -#include "os/os.h" -#include "variant_parser.h" +#include "core/bind/core_bind.h" +#include "core/core_string_names.h" +#include "core/io/file_access_network.h" +#include "core/io/file_access_pack.h" +#include "core/io/marshalls.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" +#include "core/os/keyboard.h" +#include "core/os/os.h" +#include "core/variant_parser.h" + #include <zlib.h> #define FORMAT_VERSION 4 @@ -59,7 +60,7 @@ String ProjectSettings::get_resource_path() const { String ProjectSettings::localize_path(const String &p_path) const { if (resource_path == "") - return p_path; //not initialied yet + return p_path; //not initialized yet if (p_path.begins_with("res://") || p_path.begins_with("user://") || (p_path.is_abs_path() && !p_path.begins_with(resource_path))) @@ -191,7 +192,7 @@ bool ProjectSettings::_get(const StringName &p_name, Variant &r_ret) const { name = feature_overrides[name]; } if (!props.has(name)) { - print_line("WARNING: not found: " + String(name)); + WARN_PRINTS("Property not found: " + String(name)); return false; } r_ret = props[name].variant; @@ -287,9 +288,28 @@ void ProjectSettings::_convert_to_last_version() { } } +/* + * This method is responsible for loading a project.godot file and/or data file + * using the following merit order: + * - If using NetworkClient, try to lookup project file or fail. + * - If --main-pack was passed by the user (`p_main_pack`), load it or fail. + * - Search for .pck file matching binary name. There are two possibilities: + * o exec_path.get_basename() + '.pck' (e.g. 'win_game.exe' -> 'win_game.pck') + * o exec_path + '.pck' (e.g. 'linux_game' -> 'linux_game.pck') + * For each tentative, if the file exists, load it or fail. + * - On relevant platforms (Android/iOS), lookup project file in OS resource path. + * If found, load it or fail. + * - Lookup project file in passed `p_path` (--path passed by the user), i.e. we + * are running from source code. + * If not found and `p_upwards` is true (--upwards passed by the user), look for + * project files in parent folders up to the system root (used to run a game + * from command line while in a subfolder). + * If a project file is found, load it or fail. + * If nothing was found, error out. + */ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bool p_upwards) { - //If looking for files in network, just use network! + // If looking for files in a network client, use it directly if (FileAccessNetworkClient::get_singleton()) { @@ -301,9 +321,7 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo return err; } - String exec_path = OS::get_singleton()->get_executable_path(); - - //Attempt with a passed main pack first + // Attempt with a user-defined main pack first if (p_main_pack != "") { @@ -319,25 +337,39 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo return err; } - //Attempt with execname.pck + // Attempt with exec_name.pck + // (This is the usual case when distributing a Godot game.) + + // Based on the OS, it can be the exec path + '.pck' (Linux w/o extension, macOS in .app bundle) + // or the exec path's basename + '.pck' (Windows). + // We need to test both possibilities as extensions for Linux binaries are optional + // (so both 'mygame.bin' and 'mygame' should be able to find 'mygame.pck'). + + String exec_path = OS::get_singleton()->get_executable_path(); + if (exec_path != "") { bool found = false; - // get our filename without our path (note, using exec_path.get_file before get_basename anymore because not all file systems have dots in their file names!) - String filebase_name = exec_path.get_file().get_basename(); + String exec_dir = exec_path.get_base_dir(); + String exec_filename = exec_path.get_file(); + String exec_basename = exec_filename.get_basename(); + + // Try to load data pack at the location of the executable + // As mentioned above, we have two potential names to attempt - // try to open at the location of executable - String datapack_name = exec_path.get_base_dir().plus_file(filebase_name) + ".pck"; - if (_load_resource_pack(datapack_name)) { + if (_load_resource_pack(exec_dir.plus_file(exec_basename + ".pck")) || + _load_resource_pack(exec_dir.plus_file(exec_filename + ".pck"))) { found = true; } else { - datapack_name = filebase_name + ".pck"; - if (_load_resource_pack(datapack_name)) { + // If we couldn't find them next to the executable, we attempt + // the current working directory. Same story, two tests. + if (_load_resource_pack(exec_basename + ".pck") || + _load_resource_pack(exec_filename + ".pck")) { found = true; } } - // if we opened our package, try and load our project... + // If we opened our package, try and load our project if (found) { Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); if (err == OK) { @@ -349,17 +381,15 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo } } - //Try to use the filesystem for files, according to OS. (only Android -when reading from pck- and iOS use this) - if (OS::get_singleton()->get_resource_dir() != "") { - //OS will call Globals->get_resource_path which will be empty if not overridden! - //if the OS would rather use somewhere else, then it will not be empty. + // Try to use the filesystem for files, according to OS. (only Android -when reading from pck- and iOS use this) + if (OS::get_singleton()->get_resource_dir() != "") { + // OS will call ProjectSettings->get_resource_path which will be empty if not overridden! + // If the OS would rather use a specific location, then it will not be empty. resource_path = OS::get_singleton()->get_resource_dir().replace("\\", "/"); - if (resource_path.length() && resource_path[resource_path.length() - 1] == '/') + if (resource_path != "" && resource_path[resource_path.length() - 1] == '/') { resource_path = resource_path.substr(0, resource_path.length() - 1); // chop end - - // data.pck and data.zip are deprecated and no longer supported, apologies. - // make sure this is loaded from the resource path + } Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary"); if (err == OK) { @@ -370,21 +400,19 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo return err; } - //Nothing was found, try to find a project.godot somewhere! + // Nothing was found, try to find a project file in provided path (`p_path`) + // or, if requested (`p_upwards`) in parent directories. DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); ERR_FAIL_COND_V(!d, ERR_CANT_CREATE); - d->change_dir(p_path); - String candidate = d->get_current_dir(); String current_dir = d->get_current_dir(); - + String candidate = current_dir; bool found = false; Error err; while (true) { - err = _load_settings_text_or_binary(current_dir.plus_file("project.godot"), current_dir.plus_file("project.binary")); if (err == OK) { // Optional, we don't mind if it fails @@ -395,10 +423,10 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo } if (p_upwards) { - // Try to load settings ascending through dirs shape! + // Try to load settings ascending through parent directories d->change_dir(".."); if (d->get_current_dir() == current_dir) - break; //not doing anything useful + break; // not doing anything useful current_dir = d->get_current_dir(); } else { break; @@ -415,6 +443,8 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo if (resource_path.length() && resource_path[resource_path.length() - 1] == '/') resource_path = resource_path.substr(0, resource_path.length() - 1); // chop end + // If we're loading a project.godot from source code, we can operate some + // ProjectSettings conversions if need be. _convert_to_last_version(); return OK; @@ -953,7 +983,8 @@ ProjectSettings::ProjectSettings() { disable_feature_overrides = false; registering_order = true; - Array va; + Array events; + Dictionary action; Ref<InputEventKey> key; Ref<InputEventJoypadButton> joyb; @@ -965,122 +996,162 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF("application/config/use_custom_user_dir", false); GLOBAL_DEF("application/config/custom_user_dir_name", ""); + action = Dictionary(); + action["deadzone"] = Variant(0.5f); + events = Array(); key.instance(); key->set_scancode(KEY_ENTER); - va.push_back(key); + events.push_back(key); key.instance(); key->set_scancode(KEY_KP_ENTER); - va.push_back(key); + events.push_back(key); key.instance(); key->set_scancode(KEY_SPACE); - va.push_back(key); + events.push_back(key); joyb.instance(); joyb->set_button_index(JOY_BUTTON_0); - va.push_back(joyb); - GLOBAL_DEF("input/ui_accept", va); + events.push_back(joyb); + action["events"] = events; + GLOBAL_DEF("input/ui_accept", action); input_presets.push_back("input/ui_accept"); - va = Array(); + action = Dictionary(); + action["deadzone"] = Variant(0.5f); + events = Array(); key.instance(); key->set_scancode(KEY_SPACE); - va.push_back(key); + events.push_back(key); joyb.instance(); joyb->set_button_index(JOY_BUTTON_3); - va.push_back(joyb); - GLOBAL_DEF("input/ui_select", va); + events.push_back(joyb); + action["events"] = events; + GLOBAL_DEF("input/ui_select", action); input_presets.push_back("input/ui_select"); - va = Array(); + action = Dictionary(); + action["deadzone"] = Variant(0.5f); + events = Array(); key.instance(); key->set_scancode(KEY_ESCAPE); - va.push_back(key); + events.push_back(key); joyb.instance(); joyb->set_button_index(JOY_BUTTON_1); - va.push_back(joyb); - GLOBAL_DEF("input/ui_cancel", va); + events.push_back(joyb); + action["events"] = events; + GLOBAL_DEF("input/ui_cancel", action); input_presets.push_back("input/ui_cancel"); - va = Array(); + action = Dictionary(); + action["deadzone"] = Variant(0.5f); + events = Array(); key.instance(); key->set_scancode(KEY_TAB); - va.push_back(key); - GLOBAL_DEF("input/ui_focus_next", va); + events.push_back(key); + action["events"] = events; + GLOBAL_DEF("input/ui_focus_next", action); input_presets.push_back("input/ui_focus_next"); - va = Array(); + action = Dictionary(); + action["deadzone"] = Variant(0.5f); + events = Array(); key.instance(); key->set_scancode(KEY_TAB); key->set_shift(true); - va.push_back(key); - GLOBAL_DEF("input/ui_focus_prev", va); + events.push_back(key); + action["events"] = events; + GLOBAL_DEF("input/ui_focus_prev", action); input_presets.push_back("input/ui_focus_prev"); - va = Array(); + action = Dictionary(); + action["deadzone"] = Variant(0.5f); + events = Array(); key.instance(); key->set_scancode(KEY_LEFT); - va.push_back(key); + events.push_back(key); joyb.instance(); joyb->set_button_index(JOY_DPAD_LEFT); - va.push_back(joyb); - GLOBAL_DEF("input/ui_left", va); + events.push_back(joyb); + action["events"] = events; + GLOBAL_DEF("input/ui_left", action); input_presets.push_back("input/ui_left"); - va = Array(); + action = Dictionary(); + action["deadzone"] = Variant(0.5f); + events = Array(); key.instance(); key->set_scancode(KEY_RIGHT); - va.push_back(key); + events.push_back(key); joyb.instance(); joyb->set_button_index(JOY_DPAD_RIGHT); - va.push_back(joyb); - GLOBAL_DEF("input/ui_right", va); + events.push_back(joyb); + action["events"] = events; + GLOBAL_DEF("input/ui_right", action); input_presets.push_back("input/ui_right"); - va = Array(); + action = Dictionary(); + action["deadzone"] = Variant(0.5f); + events = Array(); key.instance(); key->set_scancode(KEY_UP); - va.push_back(key); + events.push_back(key); joyb.instance(); joyb->set_button_index(JOY_DPAD_UP); - va.push_back(joyb); - GLOBAL_DEF("input/ui_up", va); + events.push_back(joyb); + action["events"] = events; + GLOBAL_DEF("input/ui_up", action); input_presets.push_back("input/ui_up"); - va = Array(); + action = Dictionary(); + action["deadzone"] = Variant(0.5f); + events = Array(); key.instance(); key->set_scancode(KEY_DOWN); - va.push_back(key); + events.push_back(key); joyb.instance(); joyb->set_button_index(JOY_DPAD_DOWN); - va.push_back(joyb); - GLOBAL_DEF("input/ui_down", va); + events.push_back(joyb); + action["events"] = events; + GLOBAL_DEF("input/ui_down", action); input_presets.push_back("input/ui_down"); - va = Array(); + action = Dictionary(); + action["deadzone"] = Variant(0.5f); + events = Array(); key.instance(); key->set_scancode(KEY_PAGEUP); - va.push_back(key); - GLOBAL_DEF("input/ui_page_up", va); + events.push_back(key); + action["events"] = events; + GLOBAL_DEF("input/ui_page_up", action); input_presets.push_back("input/ui_page_up"); - va = Array(); + action = Dictionary(); + action["deadzone"] = Variant(0.5f); + events = Array(); key.instance(); key->set_scancode(KEY_PAGEDOWN); - va.push_back(key); - GLOBAL_DEF("input/ui_page_down", va); + events.push_back(key); + action["events"] = events; + GLOBAL_DEF("input/ui_page_down", action); input_presets.push_back("input/ui_page_down"); - va = Array(); + action = Dictionary(); + action["deadzone"] = Variant(0.5f); + events = Array(); key.instance(); key->set_scancode(KEY_HOME); - va.push_back(key); - GLOBAL_DEF("input/ui_home", va); + events.push_back(key); + action["events"] = events; + GLOBAL_DEF("input/ui_home", action); input_presets.push_back("input/ui_home"); - va = Array(); + action = Dictionary(); + action["deadzone"] = Variant(0.5f); + events = Array(); key.instance(); key->set_scancode(KEY_END); - va.push_back(key); - GLOBAL_DEF("input/ui_end", va); + events.push_back(key); + action["events"] = events; + GLOBAL_DEF("input/ui_end", action); input_presets.push_back("input/ui_end"); //GLOBAL_DEF("display/window/handheld/orientation", "landscape"); @@ -1091,6 +1162,7 @@ ProjectSettings::ProjectSettings() { custom_prop_info["rendering/quality/intended_usage/framebuffer_allocation"] = PropertyInfo(Variant::INT, "rendering/quality/intended_usage/framebuffer_allocation", PROPERTY_HINT_ENUM, "2D,2D Without Sampling,3D,3D Without Effects"); GLOBAL_DEF("debug/settings/profiler/max_functions", 16384); + custom_prop_info["debug/settings/profiler/max_functions"] = PropertyInfo(Variant::INT, "debug/settings/profiler/max_functions", PROPERTY_HINT_RANGE, "128,65535,1"); //assigning here, because using GLOBAL_GET on every block for compressing can be slow Compression::zstd_long_distance_matching = GLOBAL_DEF("compression/formats/zstd/long_distance_matching", false); diff --git a/core/project_settings.h b/core/project_settings.h index 75ebc5acc8..611355f2ef 100644 --- a/core/project_settings.h +++ b/core/project_settings.h @@ -31,9 +31,10 @@ #ifndef GLOBAL_CONFIG_H #define GLOBAL_CONFIG_H -#include "object.h" -#include "os/thread_safe.h" -#include "set.h" +#include "core/object.h" +#include "core/os/thread_safe.h" +#include "core/set.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ diff --git a/core/ref_ptr.cpp b/core/ref_ptr.cpp index e3ef817df1..7f0cc0cd75 100644 --- a/core/ref_ptr.cpp +++ b/core/ref_ptr.cpp @@ -30,8 +30,8 @@ #include "ref_ptr.h" -#include "reference.h" -#include "resource.h" +#include "core/reference.h" +#include "core/resource.h" void RefPtr::operator=(const RefPtr &p_other) { diff --git a/core/ref_ptr.h b/core/ref_ptr.h index a074718d22..3f15c680b8 100644 --- a/core/ref_ptr.h +++ b/core/ref_ptr.h @@ -36,7 +36,7 @@ * It's basically an opaque container of a Reference reference, so Variant can use it. */ -#include "rid.h" +#include "core/rid.h" class RefPtr { diff --git a/core/reference.cpp b/core/reference.cpp index c33a7c683c..b79ad0bf3d 100644 --- a/core/reference.cpp +++ b/core/reference.cpp @@ -30,7 +30,7 @@ #include "reference.h" -#include "script_language.h" +#include "core/script_language.h" bool Reference::init_ref() { @@ -66,8 +66,17 @@ int Reference::reference_get_count() const { bool Reference::reference() { bool success = refcount.ref(); - if (success && get_script_instance()) { - get_script_instance()->refcount_incremented(); + if (success && refcount.get() <= 2 /* higher is not relevant */) { + if (get_script_instance()) { + get_script_instance()->refcount_incremented(); + } + if (instance_binding_count > 0) { + for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) { + if (_script_instance_bindings[i]) { + ScriptServer::get_language(i)->refcount_incremented_instance_binding(this); + } + } + } } return success; @@ -77,9 +86,19 @@ bool Reference::unreference() { bool die = refcount.unref(); - if (get_script_instance()) { - bool script_ret = get_script_instance()->refcount_decremented(); - die = die && script_ret; + if (refcount.get() <= 1 /* higher is not relevant */) { + if (get_script_instance()) { + bool script_ret = get_script_instance()->refcount_decremented(); + die = die && script_ret; + } + if (instance_binding_count > 0) { + for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) { + if (_script_instance_bindings[i]) { + bool script_ret = ScriptServer::get_language(i)->refcount_decremented_instance_binding(this); + die = die && script_ret; + } + } + } } return die; diff --git a/core/reference.h b/core/reference.h index 0d6b1ced6e..4c607fed87 100644 --- a/core/reference.h +++ b/core/reference.h @@ -31,10 +31,10 @@ #ifndef REFERENCE_H #define REFERENCE_H -#include "class_db.h" -#include "object.h" -#include "ref_ptr.h" -#include "safe_refcount.h" +#include "core/class_db.h" +#include "core/object.h" +#include "core/ref_ptr.h" +#include "core/safe_refcount.h" /** @author Juan Linietsky <reduzio@gmail.com> @@ -87,6 +87,13 @@ class Ref { //virtual Reference * get_reference() const { return reference; } public: + _FORCE_INLINE_ bool operator==(const T *p_ptr) const { + return reference == p_ptr; + } + _FORCE_INLINE_ bool operator!=(const T *p_ptr) const { + return reference != p_ptr; + } + _FORCE_INLINE_ bool operator<(const Ref<T> &p_r) const { return reference < p_r.reference; diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 0e7e63dbd8..6b776cb0b1 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -30,39 +30,41 @@ #include "register_core_types.h" -#include "bind/core_bind.h" -#include "class_db.h" -#include "compressed_translation.h" +#include "core/bind/core_bind.h" +#include "core/class_db.h" +#include "core/compressed_translation.h" +#include "core/core_string_names.h" +#include "core/engine.h" +#include "core/func_ref.h" +#include "core/input_map.h" +#include "core/io/config_file.h" +#include "core/io/http_client.h" +#include "core/io/image_loader.h" +#include "core/io/marshalls.h" +#include "core/io/multiplayer_api.h" +#include "core/io/networked_multiplayer_peer.h" +#include "core/io/packet_peer.h" +#include "core/io/packet_peer_udp.h" +#include "core/io/pck_packer.h" +#include "core/io/resource_format_binary.h" +#include "core/io/resource_import.h" +#include "core/io/stream_peer_ssl.h" +#include "core/io/tcp_server.h" +#include "core/io/translation_loader_po.h" #include "core/io/xml_parser.h" -#include "core_string_names.h" -#include "engine.h" -#include "func_ref.h" -#include "geometry.h" -#include "input_map.h" -#include "io/config_file.h" -#include "io/http_client.h" -#include "io/image_loader.h" -#include "io/marshalls.h" -#include "io/multiplayer_api.h" -#include "io/networked_multiplayer_peer.h" -#include "io/packet_peer.h" -#include "io/packet_peer_udp.h" -#include "io/pck_packer.h" -#include "io/resource_format_binary.h" -#include "io/resource_import.h" -#include "io/stream_peer_ssl.h" -#include "io/tcp_server.h" -#include "io/translation_loader_po.h" -#include "math/a_star.h" -#include "math/triangle_mesh.h" -#include "os/input.h" -#include "os/main_loop.h" -#include "packed_data_container.h" -#include "path_remap.h" -#include "project_settings.h" -#include "translation.h" - -#include "undo_redo.h" +#include "core/math/a_star.h" +#include "core/math/expression.h" +#include "core/math/geometry.h" +#include "core/math/random_number_generator.h" +#include "core/math/triangle_mesh.h" +#include "core/os/input.h" +#include "core/os/main_loop.h" +#include "core/packed_data_container.h" +#include "core/path_remap.h" +#include "core/project_settings.h" +#include "core/translation.h" +#include "core/undo_redo.h" + static ResourceFormatSaverBinary *resource_saver_binary = NULL; static ResourceFormatLoaderBinary *resource_loader_binary = NULL; static ResourceFormatImporter *resource_format_importer = NULL; @@ -145,9 +147,9 @@ void register_core_types() { ClassDB::register_class<FuncRef>(); ClassDB::register_virtual_class<StreamPeer>(); ClassDB::register_class<StreamPeerBuffer>(); - ClassDB::register_custom_instance_class<StreamPeerTCP>(); - ClassDB::register_custom_instance_class<TCP_Server>(); - ClassDB::register_custom_instance_class<PacketPeerUDP>(); + ClassDB::register_class<StreamPeerTCP>(); + ClassDB::register_class<TCP_Server>(); + ClassDB::register_class<PacketPeerUDP>(); ClassDB::register_custom_instance_class<StreamPeerSSL>(); ClassDB::register_virtual_class<IP>(); ClassDB::register_virtual_class<PacketPeer>(); @@ -155,7 +157,6 @@ void register_core_types() { ClassDB::register_virtual_class<NetworkedMultiplayerPeer>(); ClassDB::register_class<MultiplayerAPI>(); ClassDB::register_class<MainLoop>(); - //ClassDB::register_type<OptimizedSaver>(); ClassDB::register_class<Translation>(); ClassDB::register_class<PHashTranslation>(); ClassDB::register_class<UndoRedo>(); @@ -180,6 +181,7 @@ void register_core_types() { ClassDB::register_virtual_class<PackedDataContainerRef>(); ClassDB::register_class<AStar>(); ClassDB::register_class<EncodedObjectAsID>(); + ClassDB::register_class<RandomNumberGenerator>(); ClassDB::register_class<JSONParseResult>(); @@ -199,6 +201,7 @@ void register_core_types() { void register_core_settings() { //since in register core types, globals may not e present GLOBAL_DEF_RST("network/limits/packet_peer_stream/max_buffer_po2", (16)); + ProjectSettings::get_singleton()->set_custom_property_info("network/limits/packet_peer_stream/max_buffer_po2", PropertyInfo(Variant::INT, "network/limits/packet_peer_stream/max_buffer_po2", PROPERTY_HINT_RANGE, "0,64,1,or_greater")); } void register_core_singletons() { @@ -216,6 +219,7 @@ void register_core_singletons() { ClassDB::register_virtual_class<Input>(); ClassDB::register_class<InputMap>(); ClassDB::register_class<_JSON>(); + ClassDB::register_class<Expression>(); Engine::get_singleton()->add_singleton(Engine::Singleton("ProjectSettings", ProjectSettings::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("IP", IP::get_singleton())); diff --git a/core/resource.cpp b/core/resource.cpp index 87ff4d3c2a..a76e16232e 100644 --- a/core/resource.cpp +++ b/core/resource.cpp @@ -30,11 +30,12 @@ #include "resource.h" -#include "core_string_names.h" -#include "io/resource_loader.h" -#include "os/file_access.h" +#include "core/core_string_names.h" +#include "core/io/resource_loader.h" +#include "core/os/file_access.h" +#include "core/script_language.h" #include "scene/main/node.h" //only so casting works -#include "script_language.h" + #include <stdio.h> void Resource::emit_changed() { @@ -151,7 +152,7 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, Map<Ref<Res List<PropertyInfo> plist; get_property_list(&plist); - Resource *r = (Resource *)ClassDB::instance(get_class()); + Resource *r = Object::cast_to<Resource>(ClassDB::instance(get_class())); ERR_FAIL_COND_V(!r, Ref<Resource>()); r->local_scene = p_for_scene; @@ -182,7 +183,9 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, Map<Ref<Res r->set(E->get().name, p); } - return Ref<Resource>(r); + RES res = Ref<Resource>(r); + + return res; } void Resource::configure_for_local_scene(Node *p_for_scene, Map<Ref<Resource>, Ref<Resource> > &remap_cache) { @@ -228,7 +231,7 @@ Ref<Resource> Resource::duplicate(bool p_subresources) const { Variant p = get(E->get().name); if ((p.get_type() == Variant::DICTIONARY || p.get_type() == Variant::ARRAY)) { - p = p.duplicate(p_subresources); //does not make a long of sense but should work? + r->set(E->get().name, p.duplicate(p_subresources)); } else if (p.get_type() == Variant::OBJECT && (p_subresources || (E->get().usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE))) { RES sr = p; @@ -376,9 +379,9 @@ void Resource::_bind_methods() { ClassDB::bind_method(D_METHOD("duplicate", "subresources"), &Resource::duplicate, DEFVAL(false)); ADD_SIGNAL(MethodInfo("changed")); ADD_GROUP("Resource", "resource_"); - ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "resource_local_to_scene"), "set_local_to_scene", "is_local_to_scene"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "resource_local_to_scene"), "set_local_to_scene", "is_local_to_scene"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "resource_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_path", "get_path"); - ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "resource_name"), "set_name", "get_name"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "resource_name"), "set_name", "get_name"); BIND_VMETHOD(MethodInfo("_setup_local_to_scene")); } diff --git a/core/resource.h b/core/resource.h index 60c63bfe7f..b2812c11e1 100644 --- a/core/resource.h +++ b/core/resource.h @@ -31,12 +31,12 @@ #ifndef RESOURCE_H #define RESOURCE_H -#include "class_db.h" -#include "object.h" -#include "ref_ptr.h" -#include "reference.h" -#include "safe_refcount.h" -#include "self_list.h" +#include "core/class_db.h" +#include "core/object.h" +#include "core/ref_ptr.h" +#include "core/reference.h" +#include "core/safe_refcount.h" +#include "core/self_list.h" /** @author Juan Linietsky <reduzio@gmail.com> diff --git a/core/rid.h b/core/rid.h index 42306aea36..81d5b45d21 100644 --- a/core/rid.h +++ b/core/rid.h @@ -31,11 +31,11 @@ #ifndef RID_H #define RID_H -#include "list.h" -#include "os/memory.h" -#include "safe_refcount.h" -#include "set.h" -#include "typedefs.h" +#include "core/list.h" +#include "core/os/memory.h" +#include "core/safe_refcount.h" +#include "core/set.h" +#include "core/typedefs.h" /** @author Juan Linietsky <reduzio@gmail.com> @@ -118,7 +118,6 @@ protected: p_rid._data->_owner = NULL; } -# #endif public: diff --git a/core/ring_buffer.h b/core/ring_buffer.h index 00628a4ab3..54486f8cad 100644 --- a/core/ring_buffer.h +++ b/core/ring_buffer.h @@ -31,7 +31,7 @@ #ifndef RINGBUFFER_H #define RINGBUFFER_H -#include "vector.h" +#include "core/vector.h" template <typename T> class RingBuffer { @@ -135,6 +135,12 @@ public: return p_n; }; + inline int decrease_write(int p_n) { + p_n = MIN(p_n, data_left()); + inc(write_pos, size_mask + 1 - p_n); + return p_n; + } + Error write(const T &p_v) { ERR_FAIL_COND_V(space_left() < 1, FAILED); data.write[inc(write_pos, 1)] = p_v; diff --git a/core/safe_refcount.h b/core/safe_refcount.h index 36bcf5e576..940d31b2cb 100644 --- a/core/safe_refcount.h +++ b/core/safe_refcount.h @@ -31,11 +31,9 @@ #ifndef SAFE_REFCOUNT_H #define SAFE_REFCOUNT_H -#include "os/mutex.h" -/* x86/x86_64 GCC */ - +#include "core/os/mutex.h" +#include "core/typedefs.h" #include "platform_config.h" -#include "typedefs.h" // Atomic functions, these are used for multithread safe reference counters! diff --git a/core/script_debugger_local.cpp b/core/script_debugger_local.cpp index 6949b5802b..7093c3ebdb 100644 --- a/core/script_debugger_local.cpp +++ b/core/script_debugger_local.cpp @@ -29,9 +29,9 @@ /*************************************************************************/ #include "script_debugger_local.h" -#include "scene/main/scene_tree.h" -#include "os/os.h" +#include "core/os/os.h" +#include "scene/main/scene_tree.h" void ScriptDebuggerLocal::debug(ScriptLanguage *p_script, bool p_can_continue) { diff --git a/core/script_debugger_local.h b/core/script_debugger_local.h index 7eea6ef215..1f7b5c590c 100644 --- a/core/script_debugger_local.h +++ b/core/script_debugger_local.h @@ -31,8 +31,8 @@ #ifndef SCRIPT_DEBUGGER_LOCAL_H #define SCRIPT_DEBUGGER_LOCAL_H -#include "list.h" -#include "script_language.h" +#include "core/list.h" +#include "core/script_language.h" class ScriptDebuggerLocal : public ScriptDebugger { diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp index c5daaeea47..a03ddd0983 100644 --- a/core/script_debugger_remote.cpp +++ b/core/script_debugger_remote.cpp @@ -30,12 +30,12 @@ #include "script_debugger_remote.h" -#include "engine.h" -#include "io/ip.h" -#include "io/marshalls.h" -#include "os/input.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/engine.h" +#include "core/io/ip.h" +#include "core/io/marshalls.h" +#include "core/os/input.h" +#include "core/os/os.h" +#include "core/project_settings.h" #include "scene/main/node.h" #include "scene/resources/packed_scene.h" @@ -77,57 +77,27 @@ Error ScriptDebuggerRemote::connect_to_host(const String &p_host, uint16_t p_por for (int i = 0; i < tries; i++) { if (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED) { + print_verbose("Remote Debugger: Connected!"); break; } else { const int ms = waits[i]; OS::get_singleton()->delay_usec(ms * 1000); - print_line("Remote Debugger: Connection failed with status: '" + String::num(tcp_client->get_status()) + "', retrying in " + String::num(ms) + " msec."); + print_verbose("Remote Debugger: Connection failed with status: '" + String::num(tcp_client->get_status()) + "', retrying in " + String::num(ms) + " msec."); }; }; if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) { - print_line("Remote Debugger: Unable to connect"); + ERR_PRINTS("Remote Debugger: Unable to connect. Status: " + String::num(tcp_client->get_status())); return FAILED; }; - // print_line("Remote Debugger: Connection OK!"); packet_peer_stream->set_stream_peer(tcp_client); return OK; } -static int _ScriptDebuggerRemote_found_id = 0; -static Object *_ScriptDebuggerRemote_find = NULL; -static void _ScriptDebuggerRemote_debug_func(Object *p_obj) { - - if (_ScriptDebuggerRemote_find == p_obj) { - _ScriptDebuggerRemote_found_id = p_obj->get_instance_id(); - } -} - -static ObjectID safe_get_instance_id(const Variant &p_v) { - - Object *o = p_v; - if (o == NULL) - return 0; - else { - - REF r = p_v; - if (r.is_valid()) { - - return r->get_instance_id(); - } else { - - _ScriptDebuggerRemote_found_id = 0; - _ScriptDebuggerRemote_find = NULL; - ObjectDB::debug_objects(_ScriptDebuggerRemote_debug_func); - return _ScriptDebuggerRemote_found_id; - } - } -} - void ScriptDebuggerRemote::_put_variable(const String &p_name, const Variant &p_variable) { packet_peer_stream->put_var(p_name); @@ -138,7 +108,7 @@ void ScriptDebuggerRemote::_put_variable(const String &p_name, const Variant &p_ } int len = 0; - Error err = encode_variant(var, NULL, len); + Error err = encode_variant(var, NULL, len, true); if (err != OK) ERR_PRINT("Failed to encode variant"); @@ -608,8 +578,14 @@ void ScriptDebuggerRemote::_send_object_id(ObjectID p_id) { for (ScriptConstantsMap::Element *sc = constants.front(); sc; sc = sc->next()) { for (Map<StringName, Variant>::Element *E = sc->get().front(); E; E = E->next()) { String script_path = sc->key() == si->get_script().ptr() ? "" : sc->key()->get_path().get_file() + "/"; - PropertyInfo pi(E->value().get_type(), "Constants/" + script_path + E->key()); - properties.push_back(PropertyDesc(pi, E->value())); + if (E->value().get_type() == Variant::OBJECT) { + Variant id = ((Object *)E->value())->get_instance_id(); + PropertyInfo pi(id.get_type(), "Constants/" + E->key(), PROPERTY_HINT_OBJECT_ID, "Object"); + properties.push_back(PropertyDesc(pi, id)); + } else { + PropertyInfo pi(E->value().get_type(), "Constants/" + script_path + E->key()); + properties.push_back(PropertyDesc(pi, E->value())); + } } } } @@ -622,8 +598,14 @@ void ScriptDebuggerRemote::_send_object_id(ObjectID p_id) { Map<StringName, Variant> constants; s->get_constants(&constants); for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) { - PropertyInfo pi(E->value().get_type(), String("Constants/") + E->key()); - properties.push_front(PropertyDesc(pi, E->value())); + if (E->value().get_type() == Variant::OBJECT) { + Variant id = ((Object *)E->value())->get_instance_id(); + PropertyInfo pi(id.get_type(), "Constants/" + E->key(), PROPERTY_HINT_OBJECT_ID, "Object"); + properties.push_front(PropertyDesc(pi, E->value())); + } else { + PropertyInfo pi(E->value().get_type(), String("Constants/") + E->key()); + properties.push_front(PropertyDesc(pi, E->value())); + } } } } @@ -662,11 +644,13 @@ void ScriptDebuggerRemote::_send_object_id(ObjectID p_id) { prop.push_back(Variant()); } else { prop.push_back(pi.hint); - if (res.is_null()) - prop.push_back(pi.hint_string); - else - prop.push_back(String("RES:") + res->get_path()); + prop.push_back(pi.hint_string); prop.push_back(pi.usage); + + if (!res.is_null()) { + var = res->get_path(); + } + prop.push_back(var); } send_props.push_back(prop); @@ -1085,18 +1069,18 @@ ScriptDebuggerRemote::ScriptDebuggerRemote() : max_frame_functions(16), skip_profile_frame(false), reload_all_scripts(false), - tcp_client(StreamPeerTCP::create_ref()), + tcp_client(Ref<StreamPeerTCP>(memnew(StreamPeerTCP))), packet_peer_stream(Ref<PacketPeerStream>(memnew(PacketPeerStream))), last_perf_time(0), performance(Engine::get_singleton()->get_singleton_object("Performance")), requested_quit(false), mutex(Mutex::create()), - max_cps(GLOBAL_GET("network/limits/debugger_stdout/max_chars_per_second")), max_messages_per_frame(GLOBAL_GET("network/limits/debugger_stdout/max_messages_per_frame")), - max_errors_per_frame(GLOBAL_GET("network/limits/debugger_stdout/max_errors_per_frame")), - char_count(0), n_messages_dropped(0), + max_errors_per_frame(GLOBAL_GET("network/limits/debugger_stdout/max_errors_per_frame")), n_errors_dropped(0), + max_cps(GLOBAL_GET("network/limits/debugger_stdout/max_chars_per_second")), + char_count(0), last_msec(0), msec_count(0), allow_focus_steal_pid(0), @@ -1116,7 +1100,7 @@ ScriptDebuggerRemote::ScriptDebuggerRemote() : eh.userdata = this; add_error_handler(&eh); - profile_info.resize(CLAMP(int(ProjectSettings::get_singleton()->get("debug/settings/profiler/max_functions")), 128, 65535)); + profile_info.resize(GLOBAL_GET("debug/settings/profiler/max_functions")); profile_info_ptrs.resize(profile_info.size()); } diff --git a/core/script_debugger_remote.h b/core/script_debugger_remote.h index b68fc4f9c9..5cd3a23a37 100644 --- a/core/script_debugger_remote.h +++ b/core/script_debugger_remote.h @@ -31,11 +31,11 @@ #ifndef SCRIPT_DEBUGGER_REMOTE_H #define SCRIPT_DEBUGGER_REMOTE_H -#include "io/packet_peer.h" -#include "io/stream_peer_tcp.h" -#include "list.h" -#include "os/os.h" -#include "script_language.h" +#include "core/io/packet_peer.h" +#include "core/io/stream_peer_tcp.h" +#include "core/list.h" +#include "core/os/os.h" +#include "core/script_language.h" class ScriptDebuggerRemote : public ScriptDebugger { diff --git a/core/script_language.cpp b/core/script_language.cpp index 37ba3cfc62..496521486e 100644 --- a/core/script_language.cpp +++ b/core/script_language.cpp @@ -29,7 +29,8 @@ /*************************************************************************/ #include "script_language.h" -#include "project_settings.h" + +#include "core/project_settings.h" ScriptLanguage *ScriptServer::_languages[MAX_LANGUAGES]; int ScriptServer::_language_count = 0; @@ -255,6 +256,17 @@ void ScriptInstance::call_multilevel_reversed(const StringName &p_method, const call(p_method, p_args, p_argcount, ce); // script may not support multilevel calls } +void ScriptInstance::property_set_fallback(const StringName &, const Variant &, bool *r_valid) { + if (r_valid) + *r_valid = false; +} + +Variant ScriptInstance::property_get_fallback(const StringName &, bool *r_valid) { + if (r_valid) + *r_valid = false; + return Variant(); +} + void ScriptInstance::call_multilevel(const StringName &p_method, VARIANT_ARG_DECLARE) { VARIANT_ARGPTRS; @@ -364,6 +376,9 @@ ScriptDebugger::ScriptDebugger() { bool PlaceHolderScriptInstance::set(const StringName &p_name, const Variant &p_value) { + if (build_failed) + return false; + if (values.has(p_name)) { Variant defval; if (script->get_property_default_value(p_name, defval)) { @@ -392,22 +407,31 @@ bool PlaceHolderScriptInstance::get(const StringName &p_name, Variant &r_ret) co return true; } - Variant defval; - if (script->get_property_default_value(p_name, defval)) { - r_ret = defval; - return true; + if (!build_failed) { + Variant defval; + if (script->get_property_default_value(p_name, defval)) { + r_ret = defval; + return true; + } } + return false; } void PlaceHolderScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const { - for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) { - PropertyInfo pinfo = E->get(); - if (!values.has(pinfo.name)) { - pinfo.usage |= PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE; + if (build_failed) { + for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) { + p_properties->push_back(E->get()); + } + } else { + for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) { + PropertyInfo pinfo = E->get(); + if (!values.has(pinfo.name)) { + pinfo.usage |= PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE; + } + p_properties->push_back(E->get()); } - p_properties->push_back(E->get()); } } @@ -426,12 +450,18 @@ Variant::Type PlaceHolderScriptInstance::get_property_type(const StringName &p_n void PlaceHolderScriptInstance::get_method_list(List<MethodInfo> *p_list) const { + if (build_failed) + return; + if (script.is_valid()) { script->get_script_method_list(p_list); } } bool PlaceHolderScriptInstance::has_method(const StringName &p_method) const { + if (build_failed) + return false; + if (script.is_valid()) { return script->has_method(p_method); } @@ -440,6 +470,8 @@ bool PlaceHolderScriptInstance::has_method(const StringName &p_method) const { void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, const Map<StringName, Variant> &p_values) { + build_failed = false; + Set<StringName> new_values; for (const List<PropertyInfo>::Element *E = p_properties.front(); E; E = E->next()) { @@ -483,10 +515,56 @@ void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, c //change notify } +void PlaceHolderScriptInstance::property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid) { + + if (build_failed) { + Map<StringName, Variant>::Element *E = values.find(p_name); + + if (E) { + E->value() = p_value; + } else { + values.insert(p_name, p_value); + } + + bool found = false; + for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) { + if (E->get().name == p_name) { + found = true; + break; + } + } + if (!found) { + properties.push_back(PropertyInfo(p_value.get_type(), p_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_SCRIPT_VARIABLE)); + } + } + + if (r_valid) + *r_valid = false; // Cannot change the value in either case +} + +Variant PlaceHolderScriptInstance::property_get_fallback(const StringName &p_name, bool *r_valid) { + + if (build_failed) { + const Map<StringName, Variant>::Element *E = values.find(p_name); + + if (E) { + if (r_valid) + *r_valid = true; + return E->value(); + } + } + + if (r_valid) + *r_valid = false; + + return Variant(); +} + PlaceHolderScriptInstance::PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script, Object *p_owner) : owner(p_owner), language(p_language), - script(p_script) { + script(p_script), + build_failed(false) { } PlaceHolderScriptInstance::~PlaceHolderScriptInstance() { diff --git a/core/script_language.h b/core/script_language.h index 4e81b9b626..654d1d4265 100644 --- a/core/script_language.h +++ b/core/script_language.h @@ -31,10 +31,11 @@ #ifndef SCRIPT_LANGUAGE_H #define SCRIPT_LANGUAGE_H -#include "io/multiplayer_api.h" -#include "map.h" -#include "pair.h" -#include "resource.h" +#include "core/io/multiplayer_api.h" +#include "core/map.h" +#include "core/pair.h" +#include "core/resource.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -115,6 +116,7 @@ public: virtual StringName get_instance_base_type() const = 0; // this may not work in all scripts, will return empty if so virtual ScriptInstance *instance_create(Object *p_this) = 0; + virtual PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this) { return NULL; } virtual bool instance_has(const Object *p_this) const = 0; virtual bool has_source_code() const = 0; @@ -126,6 +128,7 @@ public: virtual MethodInfo get_method_info(const StringName &p_method) const = 0; virtual bool is_tool() const = 0; + virtual bool is_valid() const = 0; virtual ScriptLanguage *get_language() const = 0; @@ -176,6 +179,9 @@ public: virtual bool is_placeholder() const { return false; } + virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid); + virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid); + virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const = 0; virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const = 0; @@ -207,13 +213,20 @@ public: virtual void finish() = 0; /* EDITOR FUNCTIONS */ + struct Warning { + int line; + int code; + String string_code; + String message; + }; + virtual void get_reserved_words(List<String> *p_words) const = 0; virtual void get_comment_delimiters(List<String> *p_delimiters) const = 0; virtual void get_string_delimiters(List<String> *p_delimiters) const = 0; virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const = 0; 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, Set<int> *r_safe_lines = NULL) const = 0; + 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, List<Warning> *r_warnings = NULL, Set<int> *r_safe_lines = 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; @@ -300,11 +313,13 @@ public: virtual void *alloc_instance_binding_data(Object *p_object) { return NULL; } //optional, not used by all languages virtual void free_instance_binding_data(void *p_data) {} //optional, not used by all languages + virtual void refcount_incremented_instance_binding(Object *p_object) {} //optional, not used by all languages + virtual bool refcount_decremented_instance_binding(Object *p_object) { return true; } //return true if it can die //optional, not used by all languages virtual void frame(); virtual bool handles_global_class_type(const String &p_type) const { return false; } - virtual String get_global_class_name(const String &p_path, String *r_base_type = NULL) const { return String(); } + virtual String get_global_class_name(const String &p_path, String *r_base_type = NULL, String *r_icon_path = NULL) const { return String(); } virtual ~ScriptLanguage() {} }; @@ -319,6 +334,8 @@ class PlaceHolderScriptInstance : public ScriptInstance { ScriptLanguage *language; Ref<Script> script; + bool build_failed; + public: virtual bool set(const StringName &p_name, const Variant &p_value); virtual bool get(const StringName &p_name, Variant &r_ret) const; @@ -344,8 +361,14 @@ public: void update(const List<PropertyInfo> &p_properties, const Map<StringName, Variant> &p_values); //likely changed in editor + void set_build_failed(bool p_build_failed) { build_failed = p_build_failed; } + bool get_build_failed() const { return build_failed; } + virtual bool is_placeholder() const { return true; } + virtual void property_set_fallback(const StringName &p_name, const Variant &p_value, bool *r_valid); + virtual Variant property_get_fallback(const StringName &p_name, bool *r_valid); + virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const { return MultiplayerAPI::RPC_MODE_DISABLED; } virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const { return MultiplayerAPI::RPC_MODE_DISABLED; } diff --git a/core/self_list.h b/core/self_list.h index 6e84e1cd5f..9a4ecdeef9 100644 --- a/core/self_list.h +++ b/core/self_list.h @@ -31,7 +31,7 @@ #ifndef SELF_LIST_H #define SELF_LIST_H -#include "typedefs.h" +#include "core/typedefs.h" template <class T> class SelfList { diff --git a/core/set.h b/core/set.h index d79dd81644..59aa54128e 100644 --- a/core/set.h +++ b/core/set.h @@ -31,8 +31,8 @@ #ifndef SET_H #define SET_H -#include "os/memory.h" -#include "typedefs.h" +#include "core/os/memory.h" +#include "core/typedefs.h" /** @author Juan Linietsky <reduzio@gmail.com> @@ -595,6 +595,7 @@ public: return e; } + inline bool empty() const { return _data.size_cache == 0; } inline int size() const { return _data.size_cache; } int calculate_depth() const { diff --git a/core/sort.h b/core/sort.h index a6780309d8..e112304d1b 100644 --- a/core/sort.h +++ b/core/sort.h @@ -31,18 +31,27 @@ #ifndef SORT_H #define SORT_H -#include "typedefs.h" -/** - @author ,,, <red@lunatea> -*/ +#include "core/typedefs.h" + +#define ERR_BAD_COMPARE(cond) \ + if (unlikely(cond)) { \ + ERR_PRINT("bad comparison function; sorting will be broken"); \ + break; \ + } template <class T> struct _DefaultComparator { - inline bool operator()(const T &a, const T &b) const { return (a < b); } + _FORCE_INLINE_ bool operator()(const T &a, const T &b) const { return (a < b); } }; -template <class T, class Comparator = _DefaultComparator<T> > +#ifdef DEBUG_ENABLED +#define SORT_ARRAY_VALIDATE_ENABLED true +#else +#define SORT_ARRAY_VALIDATE_ENABLED false +#endif + +template <class T, class Comparator = _DefaultComparator<T>, bool Validate = SORT_ARRAY_VALIDATE_ENABLED> class SortArray { enum { @@ -164,12 +173,23 @@ public: inline int partitioner(int p_first, int p_last, T p_pivot, T *p_array) const { + const int unmodified_first = p_first; + const int unmodified_last = p_last; + while (true) { - while (compare(p_array[p_first], p_pivot)) + while (compare(p_array[p_first], p_pivot)) { + if (Validate) { + ERR_BAD_COMPARE(p_first == unmodified_last - 1) + } p_first++; + } p_last--; - while (compare(p_pivot, p_array[p_last])) + while (compare(p_pivot, p_array[p_last])) { + if (Validate) { + ERR_BAD_COMPARE(p_last == unmodified_first) + } p_last--; + } if (!(p_first < p_last)) return p_first; @@ -238,6 +258,9 @@ public: int next = p_last - 1; while (compare(p_value, p_array[next])) { + if (Validate) { + ERR_BAD_COMPARE(next == 0) + } p_array[p_last] = p_array[next]; p_last = next; next--; diff --git a/core/string_buffer.h b/core/string_buffer.h index 5d3be0ecf1..38dce5e6bf 100644 --- a/core/string_buffer.h +++ b/core/string_buffer.h @@ -31,8 +31,7 @@ #ifndef STRING_BUFFER_H #define STRING_BUFFER_H -#include "ustring.h" -#include <string.h> +#include "core/ustring.h" template <int SHORT_BUFFER_SIZE = 64> class StringBuffer { diff --git a/core/string_db.cpp b/core/string_db.cpp index 2475cbe3e8..a798df8795 100644 --- a/core/string_db.cpp +++ b/core/string_db.cpp @@ -30,8 +30,8 @@ #include "string_db.h" -#include "os/os.h" -#include "print_string.h" +#include "core/os/os.h" +#include "core/print_string.h" StaticCString StaticCString::create(const char *p_ptr) { StaticCString scs; @@ -73,7 +73,6 @@ void StringName::cleanup() { _Data *d = _table[i]; lost_strings++; if (OS::get_singleton()->is_stdout_verbose()) { - if (d->cname) { print_line("Orphan StringName: " + String(d->cname)); } else { @@ -85,8 +84,8 @@ void StringName::cleanup() { memdelete(d); } } - if (OS::get_singleton()->is_stdout_verbose() && lost_strings) { - print_line("StringName: " + itos(lost_strings) + " unclaimed string names at exit."); + if (lost_strings) { + print_verbose("StringName: " + itos(lost_strings) + " unclaimed string names at exit."); } lock->unlock(); diff --git a/core/string_db.h b/core/string_db.h index 965385b136..46bb77df93 100644 --- a/core/string_db.h +++ b/core/string_db.h @@ -31,9 +31,9 @@ #ifndef STRING_DB_H #define STRING_DB_H -#include "os/mutex.h" -#include "safe_refcount.h" -#include "ustring.h" +#include "core/os/mutex.h" +#include "core/safe_refcount.h" +#include "core/ustring.h" /** @author Juan Linietsky <reduzio@gmail.com> */ diff --git a/core/translation.cpp b/core/translation.cpp index 78115c3749..25e67e9b96 100644 --- a/core/translation.cpp +++ b/core/translation.cpp @@ -30,9 +30,9 @@ #include "translation.h" -#include "io/resource_loader.h" -#include "os/os.h" -#include "project_settings.h" +#include "core/io/resource_loader.h" +#include "core/os/os.h" +#include "core/project_settings.h" // ISO 639-1 language codes, with the addition of glibc locales with their // regional identifiers. This list must match the language names (in English) @@ -938,11 +938,14 @@ void TranslationServer::set_locale(const String &p_locale) { if (!is_locale_valid(univ_locale)) { String trimmed_locale = get_trimmed_locale(univ_locale); + print_verbose(vformat("Unsupported locale '%s', falling back to '%s'.", p_locale, trimmed_locale)); - ERR_EXPLAIN("Invalid locale: " + trimmed_locale); - ERR_FAIL_COND(!is_locale_valid(trimmed_locale)); - - locale = trimmed_locale; + if (!is_locale_valid(trimmed_locale)) { + ERR_PRINTS(vformat("Unsupported locale '%s', falling back to 'en'.", trimmed_locale)); + locale = "en"; + } else { + locale = trimmed_locale; + } } else { locale = univ_locale; } @@ -1098,7 +1101,6 @@ bool TranslationServer::_load_translations(const String &p_from) { for (int i = 0; i < tcount; i++) { - //print_line( "Loading translation from " + r[i] ); Ref<Translation> tr = ResourceLoader::load(r[i]); if (tr.is_valid()) add_translation(tr); diff --git a/core/translation.h b/core/translation.h index e7c0dcbc07..be626f4eb9 100644 --- a/core/translation.h +++ b/core/translation.h @@ -31,7 +31,7 @@ #ifndef TRANSLATION_H #define TRANSLATION_H -#include "resource.h" +#include "core/resource.h" class Translation : public Resource { diff --git a/core/typedefs.h b/core/typedefs.h index 57afa3bdb4..5c15da9c2d 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -32,8 +32,9 @@ #define TYPEDEFS_H #include <stddef.h> + /** - * Basic definitions and simple functions to be used everywhere.. + * Basic definitions and simple functions to be used everywhere. */ #include "platform_config.h" @@ -43,6 +44,7 @@ #define _MKSTR(m_x) _STR(m_x) #endif +//should always inline no matter what #ifndef _ALWAYS_INLINE_ #if defined(__GNUC__) && (__GNUC__ >= 4) @@ -57,10 +59,17 @@ #endif +//should always inline, except in some cases because it makes debugging harder #ifndef _FORCE_INLINE_ + +#ifdef DISABLE_FORCED_INLINE +#define _FORCE_INLINE_ inline +#else #define _FORCE_INLINE_ _ALWAYS_INLINE_ #endif +#endif + //custom, gcc-safe offsetof, because gcc complains a lot. template <class T> T *_nullptr() { @@ -71,7 +80,7 @@ T *_nullptr() { #define OFFSET_OF(st, m) \ ((size_t)((char *)&(_nullptr<st>()->m) - (char *)0)) /** - * Some platforms (devices) not define NULL + * Some platforms (devices) don't define NULL */ #ifndef NULL @@ -79,7 +88,7 @@ T *_nullptr() { #endif /** - * Windows defines a lot of badly stuff we'll never ever use. undefine it. + * Windows badly defines a lot of stuff we'll never use. Undefine it. */ #ifdef _WIN32 @@ -96,19 +105,19 @@ T *_nullptr() { #undef CONNECT_DEFERRED // override from Windows SDK, clashes with Object enum #endif -#include "int_types.h" +#include "core/int_types.h" -#include "error_list.h" -#include "error_macros.h" +#include "core/error_list.h" +#include "core/error_macros.h" /** Generic ABS function, for math uses please use Math::abs */ #ifndef ABS -#define ABS(m_v) ((m_v < 0) ? (-(m_v)) : (m_v)) +#define ABS(m_v) (((m_v) < 0) ? (-(m_v)) : (m_v)) #endif #ifndef SGN -#define SGN(m_v) ((m_v < 0) ? (-1.0) : (+1.0)) +#define SGN(m_v) (((m_v) < 0) ? (-1.0) : (+1.0)) #endif #ifndef MIN @@ -296,4 +305,4 @@ struct _GlobalLock { #define unlikely(x) x #endif -#endif /* typedefs.h */ +#endif // TYPEDEFS_H diff --git a/core/undo_redo.cpp b/core/undo_redo.cpp index 3d90608dd7..3d41c374ea 100644 --- a/core/undo_redo.cpp +++ b/core/undo_redo.cpp @@ -30,7 +30,7 @@ #include "undo_redo.h" -#include "os/os.h" +#include "core/os/os.h" void UndoRedo::_discard_redo() { @@ -325,7 +325,7 @@ bool UndoRedo::undo() { return true; } -void UndoRedo::clear_history() { +void UndoRedo::clear_history(bool p_increase_version) { ERR_FAIL_COND(action_level > 0); _discard_redo(); @@ -333,7 +333,8 @@ void UndoRedo::clear_history() { while (actions.size()) _pop_history_tail(); - //version++; + if (p_increase_version) + version++; } String UndoRedo::get_current_action_name() const { @@ -493,7 +494,7 @@ void UndoRedo::_bind_methods() { ClassDB::bind_method(D_METHOD("add_undo_property", "object", "property", "value"), &UndoRedo::add_undo_property); ClassDB::bind_method(D_METHOD("add_do_reference", "object"), &UndoRedo::add_do_reference); ClassDB::bind_method(D_METHOD("add_undo_reference", "object"), &UndoRedo::add_undo_reference); - ClassDB::bind_method(D_METHOD("clear_history"), &UndoRedo::clear_history); + ClassDB::bind_method(D_METHOD("clear_history", "increase_version"), &UndoRedo::clear_history, DEFVAL(true)); ClassDB::bind_method(D_METHOD("get_current_action_name"), &UndoRedo::get_current_action_name); ClassDB::bind_method(D_METHOD("get_version"), &UndoRedo::get_version); ClassDB::bind_method(D_METHOD("redo"), &UndoRedo::redo); diff --git a/core/undo_redo.h b/core/undo_redo.h index 3a17c78851..f09fca9a78 100644 --- a/core/undo_redo.h +++ b/core/undo_redo.h @@ -31,8 +31,8 @@ #ifndef UNDO_REDO_H #define UNDO_REDO_H -#include "object.h" -#include "resource.h" +#include "core/object.h" +#include "core/resource.h" class UndoRedo : public Object { @@ -112,7 +112,7 @@ public: bool redo(); bool undo(); String get_current_action_name() const; - void clear_history(); + void clear_history(bool p_increase_version = true); uint64_t get_version() const; diff --git a/core/ustring.cpp b/core/ustring.cpp index 84613610a9..3f017fa985 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -30,12 +30,13 @@ #include "ustring.h" -#include "color.h" -#include "math_funcs.h" -#include "os/memory.h" -#include "print_string.h" -#include "ucaps.h" -#include "variant.h" +#include "core/color.h" +#include "core/math/math_funcs.h" +#include "core/os/memory.h" +#include "core/print_string.h" +#include "core/translation.h" +#include "core/ucaps.h" +#include "core/variant.h" #include "thirdparty/misc/md5.h" #include "thirdparty/misc/sha256.h" @@ -48,7 +49,7 @@ #endif #if defined(MINGW_ENABLED) || defined(_MSC_VER) -#define snprintf _snprintf +#define snprintf _snprintf_s #endif #define MAX_DIGITS 6 @@ -148,7 +149,7 @@ void String::copy_from(const char *p_cstr) { } } -void String::copy_from(const CharType *p_cstr, int p_clip_to) { +void String::copy_from(const CharType *p_cstr, const int p_clip_to) { if (!p_cstr) { @@ -158,12 +159,9 @@ void String::copy_from(const CharType *p_cstr, int p_clip_to) { int len = 0; const CharType *ptr = p_cstr; - while (*(ptr++) != 0) + while ((p_clip_to < 0 || len < p_clip_to) && *(ptr++) != 0) len++; - if (p_clip_to >= 0 && len > p_clip_to) - len = p_clip_to; - if (len == 0) { resize(0); @@ -177,7 +175,7 @@ void String::copy_from(const CharType *p_cstr, int p_clip_to) { // p_char != NULL // p_length > 0 // p_length <= p_char strlen -void String::copy_from_unchecked(const CharType *p_char, int p_length) { +void String::copy_from_unchecked(const CharType *p_char, const int p_length) { resize(p_length + 1); set(p_length, 0); @@ -588,6 +586,8 @@ String String::camelcase_to_underscore(bool lowercase) const { bool is_upper = cstr[i] >= A && cstr[i] <= Z; bool is_number = cstr[i] >= '0' && cstr[i] <= '9'; bool are_next_2_lower = false; + bool is_next_lower = false; + bool is_next_number = false; bool was_precedent_upper = cstr[i - 1] >= A && cstr[i - 1] <= Z; bool was_precedent_number = cstr[i - 1] >= '0' && cstr[i - 1] <= '9'; @@ -595,7 +595,18 @@ String String::camelcase_to_underscore(bool lowercase) const { are_next_2_lower = cstr[i + 1] >= a && cstr[i + 1] <= z && cstr[i + 2] >= a && cstr[i + 2] <= z; } - bool should_split = ((is_upper && !was_precedent_upper && !was_precedent_number) || (was_precedent_upper && is_upper && are_next_2_lower) || (is_number && !was_precedent_number)); + if (i + 1 < this->size()) { + is_next_lower = cstr[i + 1] >= a && cstr[i + 1] <= z; + is_next_number = cstr[i + 1] >= '0' && cstr[i + 1] <= '9'; + } + + const bool a = is_upper && !was_precedent_upper && !was_precedent_number; + const bool b = was_precedent_upper && is_upper && are_next_2_lower; + const bool c = is_number && !was_precedent_number; + const bool can_break_number_letter = is_number && !was_precedent_number && is_next_lower; + const bool can_break_letter_number = !is_number && was_precedent_number && (is_next_lower || is_next_number); + + bool should_split = a || b || c || can_break_number_letter || can_break_letter_number; if (should_split) { new_string += this->substr(start_index, i - start_index) + "_"; start_index = i; @@ -1343,7 +1354,7 @@ String String::utf8(const char *p_utf8, int p_len) { bool String::parse_utf8(const char *p_utf8, int p_len) { -#define _UNICERROR(m_err) print_line("unicode error: " + String(m_err)); +#define _UNICERROR(m_err) print_line("Unicode error: " + String(m_err)); String aux; @@ -1830,8 +1841,8 @@ static double built_in_strtod(const C *string, /* A decimal ASCII floating-point int sign, expSign = false; double fraction, dblExp; const double *d; - register const C *p; - register int c; + const C *p; + int c; int exp = 0; /* Exponent read from "EX" field. */ int fracExp = 0; /* Exponent that derives from the fractional * part. Under normal circumstances, it is @@ -2763,16 +2774,13 @@ String String::format(const Variant &values, String placeholder) const { if (value_arr.size() == 2) { Variant v_key = value_arr[0]; - String key; - - key = v_key.get_construct_string(); + String key = v_key; if (key.left(1) == "\"" && key.right(key.length() - 1) == "\"") { key = key.substr(1, key.length() - 2); } Variant v_val = value_arr[1]; - String val; - val = v_val.get_construct_string(); + String val = v_val; if (val.left(1) == "\"" && val.right(val.length() - 1) == "\"") { val = val.substr(1, val.length() - 2); @@ -2784,14 +2792,17 @@ String String::format(const Variant &values, String placeholder) const { } } else { //Array structure ["RobotGuy","Logis","rookie"] Variant v_val = values_arr[i]; - String val; - val = v_val.get_construct_string(); + String val = v_val; if (val.left(1) == "\"" && val.right(val.length() - 1) == "\"") { val = val.substr(1, val.length() - 2); } - new_string = new_string.replace(placeholder.replace("_", i_as_str), val); + if (placeholder.find("_") > -1) { + new_string = new_string.replace(placeholder.replace("_", i_as_str), val); + } else { + new_string = new_string.replace_first(placeholder, val); + } } } } else if (values.get_type() == Variant::DICTIONARY) { @@ -2800,8 +2811,8 @@ String String::format(const Variant &values, String placeholder) const { d.get_key_list(&keys); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - String key = E->get().get_construct_string(); - String val = d[E->get()].get_construct_string(); + String key = E->get(); + String val = d[E->get()]; if (key.left(1) == "\"" && key.right(key.length() - 1) == "\"") { key = key.substr(1, key.length() - 2); @@ -3090,7 +3101,7 @@ String String::simplify_path() const { } else if (s.begins_with("user://")) { drive = "user://"; - s = s.substr(6, s.length()); + s = s.substr(7, s.length()); } else if (s.begins_with("/") || s.begins_with("\\")) { drive = s.substr(0, 1); @@ -3881,8 +3892,6 @@ String String::percent_decode() const { pe += c; } - pe += '0'; - return String::utf8(pe.ptr()); } @@ -4026,7 +4035,7 @@ String String::sprintf(const Array &values, bool *error) const { str = str.pad_decimals(min_decimals); // Show sign - if (show_sign && value >= 0) { + if (show_sign && str.left(1) != "-") { str = str.insert(0, "+"); } @@ -4205,8 +4214,6 @@ String String::unquote() const { return substr(1, length() - 2); } -#include "translation.h" - #ifdef TOOLS_ENABLED String TTR(const String &p_text) { diff --git a/core/ustring.h b/core/ustring.h index 3b4405833c..8e4dbd8031 100644 --- a/core/ustring.h +++ b/core/ustring.h @@ -28,16 +28,16 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef RSTRING_H -#define RSTRING_H +#ifndef USTRING_H +#define USTRING_H -#include "array.h" -#include "cowdata.h" -#include "typedefs.h" -#include "vector.h" +#include "core/array.h" +#include "core/cowdata.h" +#include "core/typedefs.h" +#include "core/vector.h" /** - @author red <red@killy> + @author Juan Linietsky <reduzio@gmail.com> */ class CharString { @@ -63,7 +63,7 @@ public: CharString &operator+=(char p_char); int length() const { return size() ? size() - 1 : 0; } const char *get_data() const; - operator const char *() { return get_data(); }; + operator const char *() const { return get_data(); }; }; typedef wchar_t CharType; @@ -84,9 +84,9 @@ class String { CowData<CharType> _cowdata; void copy_from(const char *p_cstr); - void copy_from(const CharType *p_cstr, int p_clip_to = -1); + void copy_from(const CharType *p_cstr, const int p_clip_to = -1); void copy_from(const CharType &p_char); - void copy_from_unchecked(const CharType *p_char, int p_length); + void copy_from_unchecked(const CharType *p_char, const int p_length); bool _base_is_subsequence_of(const String &p_string, bool case_insensitive) const; public: @@ -366,4 +366,4 @@ String RTR(const String &); bool is_symbol(CharType c); bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end); -#endif +#endif // USTRING_H diff --git a/core/variant.cpp b/core/variant.cpp index e4be5520bc..eb9f34fee6 100644 --- a/core/variant.cpp +++ b/core/variant.cpp @@ -30,14 +30,14 @@ #include "variant.h" -#include "core_string_names.h" -#include "io/marshalls.h" -#include "math_funcs.h" -#include "print_string.h" -#include "resource.h" +#include "core/core_string_names.h" +#include "core/io/marshalls.h" +#include "core/math/math_funcs.h" +#include "core/print_string.h" +#include "core/resource.h" +#include "core/variant_parser.h" #include "scene/gui/control.h" #include "scene/main/node.h" -#include "variant_parser.h" String Variant::get_type_name(Variant::Type p_type) { @@ -858,7 +858,7 @@ bool Variant::is_one() const { // atomic types case BOOL: { - return _data._bool == true; + return _data._bool; } break; case INT: { @@ -1192,7 +1192,7 @@ Variant::operator int64_t() const { case BOOL: return _data._bool ? 1 : 0; case INT: return _data._int; case REAL: return _data._real; - case STRING: return operator String().to_int(); + case STRING: return operator String().to_int64(); default: { return 0; @@ -1460,7 +1460,7 @@ Variant::operator String() const { const Dictionary &d = *reinterpret_cast<const Dictionary *>(_data._mem); //const String *K=NULL; - String str; + String str("{"); List<Variant> keys; d.get_key_list(&keys); @@ -1479,8 +1479,9 @@ Variant::operator String() const { for (int i = 0; i < pairs.size(); i++) { if (i > 0) str += ", "; - str += "(" + pairs[i].key + ":" + pairs[i].value + ")"; + str += pairs[i].key + ":" + pairs[i].value; } + str += "}"; return str; } break; @@ -1661,7 +1662,17 @@ Variant::operator Transform() const { return Transform(*_data._basis, Vector3()); else if (type == QUAT) return Transform(Basis(*reinterpret_cast<const Quat *>(_data._mem)), Vector3()); - else + else if (type == TRANSFORM2D) { + const Transform2D &t = *_data._transform2d; + Transform m; + m.basis.elements[0][0] = t.elements[0][0]; + m.basis.elements[1][0] = t.elements[0][1]; + m.basis.elements[0][1] = t.elements[1][0]; + m.basis.elements[1][1] = t.elements[1][1]; + m.origin[0] = t.elements[2][0]; + m.origin[1] = t.elements[2][1]; + return m; + } else return Transform(); } diff --git a/core/variant.h b/core/variant.h index 4b245d25e6..8953217760 100644 --- a/core/variant.h +++ b/core/variant.h @@ -35,23 +35,23 @@ @author Juan Linietsky <reduzio@gmail.com> */ -#include "aabb.h" -#include "array.h" -#include "color.h" -#include "dictionary.h" -#include "dvector.h" -#include "face3.h" -#include "io/ip_address.h" -#include "math_2d.h" -#include "matrix3.h" -#include "node_path.h" -#include "plane.h" -#include "quat.h" -#include "ref_ptr.h" -#include "rid.h" -#include "transform.h" -#include "ustring.h" -#include "vector3.h" +#include "core/array.h" +#include "core/color.h" +#include "core/dictionary.h" +#include "core/dvector.h" +#include "core/io/ip_address.h" +#include "core/math/aabb.h" +#include "core/math/face3.h" +#include "core/math/matrix3.h" +#include "core/math/plane.h" +#include "core/math/quat.h" +#include "core/math/transform.h" +#include "core/math/transform_2d.h" +#include "core/math/vector3.h" +#include "core/node_path.h" +#include "core/ref_ptr.h" +#include "core/rid.h" +#include "core/ustring.h" class RefPtr; class Object; @@ -116,7 +116,7 @@ public: }; private: - friend class _VariantCall; + friend struct _VariantCall; // Variant takes 20 bytes when real_t is float, and 36 if double // it only allocates extra memory for aabb/matrix. @@ -398,9 +398,9 @@ public: void static_assign(const Variant &p_variant); static void get_constructor_list(Variant::Type p_type, List<MethodInfo> *p_list); - static void get_numeric_constants_for_type(Variant::Type p_type, List<StringName> *p_constants); - static bool has_numeric_constant(Variant::Type p_type, const StringName &p_value); - static int get_numeric_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid = NULL); + static void get_constants_for_type(Variant::Type p_type, List<StringName> *p_constants); + static bool has_constant(Variant::Type p_type, const StringName &p_value); + static Variant get_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid = NULL); typedef String (*ObjectDeConstruct)(const Variant &p_object, void *ud); typedef void (*ObjectConstruct)(const String &p_text, void *ud, Variant &r_value); diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 20a2929dc0..0c6e43fe36 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -30,11 +30,12 @@ #include "variant.h" -#include "core_string_names.h" -#include "io/compression.h" -#include "object.h" -#include "os/os.h" -#include "script_language.h" +#include "core/color_names.inc" +#include "core/core_string_names.h" +#include "core/io/compression.h" +#include "core/object.h" +#include "core/os/os.h" +#include "core/script_language.h" typedef void (*VariantFunc)(Variant &r_ret, Variant &p_self, const Variant **p_args); typedef void (*VariantConstructFunc)(Variant &r_ret, const Variant **p_args); @@ -154,9 +155,7 @@ struct _VariantCall { funcdata.default_args = p_defaultarg; funcdata._const = p_const; funcdata.returns = p_has_return; -#ifdef DEBUG_ENABLED funcdata.return_type = p_return; -#endif if (p_argtype1.name) { funcdata.arg_types.push_back(p_argtype1.type); @@ -338,6 +337,7 @@ struct _VariantCall { VCALL_LOCALMEM0R(Vector2, is_normalized); VCALL_LOCALMEM1R(Vector2, distance_to); VCALL_LOCALMEM1R(Vector2, distance_squared_to); + VCALL_LOCALMEM1R(Vector2, project); VCALL_LOCALMEM1R(Vector2, angle_to); VCALL_LOCALMEM1R(Vector2, angle_to_point); VCALL_LOCALMEM2R(Vector2, linear_interpolate); @@ -394,6 +394,7 @@ struct _VariantCall { VCALL_LOCALMEM0R(Vector3, round); VCALL_LOCALMEM1R(Vector3, distance_to); VCALL_LOCALMEM1R(Vector3, distance_squared_to); + VCALL_LOCALMEM1R(Vector3, project); VCALL_LOCALMEM1R(Vector3, angle_to); VCALL_LOCALMEM1R(Vector3, slide); VCALL_LOCALMEM1R(Vector3, bounce); @@ -483,6 +484,7 @@ struct _VariantCall { VCALL_LOCALMEM0R(Dictionary, keys); VCALL_LOCALMEM0R(Dictionary, values); VCALL_LOCALMEM1R(Dictionary, duplicate); + VCALL_LOCALMEM2R(Dictionary, get); VCALL_LOCALMEM2(Array, set); VCALL_LOCALMEM1R(Array, get); @@ -513,6 +515,8 @@ struct _VariantCall { VCALL_LOCALMEM4R(Array, bsearch_custom); VCALL_LOCALMEM1R(Array, duplicate); VCALL_LOCALMEM0(Array, invert); + VCALL_LOCALMEM0R(Array, max); + VCALL_LOCALMEM0R(Array, min); static void _call_PoolByteArray_get_string_from_ascii(Variant &r_ret, Variant &p_self, const Variant **p_args) { @@ -991,6 +995,7 @@ struct _VariantCall { #ifdef DEBUG_ENABLED List<StringName> value_ordered; #endif + Map<StringName, Variant> variant_value; }; static ConstantData *constant_data; @@ -1002,6 +1007,11 @@ struct _VariantCall { constant_data[p_type].value_ordered.push_back(p_constant_name); #endif } + + static void add_variant_constant(int p_type, StringName p_constant_name, const Variant &p_constant_value) { + + constant_data[p_type].variant_value[p_constant_name] = p_constant_value; + } }; _VariantCall::TypeFunc *_VariantCall::type_funcs = NULL; @@ -1152,7 +1162,7 @@ Variant Variant::construct(const Variant::Type p_type, const Variant **p_args, i return Variant(bool(*p_args[0])); } case INT: { - return (int(*p_args[0])); + return (int64_t(*p_args[0])); } case REAL: { return real_t(*p_args[0]); @@ -1354,7 +1364,7 @@ void Variant::get_constructor_list(Variant::Type p_type, List<MethodInfo> *p_lis } } -void Variant::get_numeric_constants_for_type(Variant::Type p_type, List<StringName> *p_constants) { +void Variant::get_constants_for_type(Variant::Type p_type, List<StringName> *p_constants) { ERR_FAIL_INDEX(p_type, Variant::VARIANT_MAX); @@ -1370,16 +1380,21 @@ void Variant::get_numeric_constants_for_type(Variant::Type p_type, List<StringNa p_constants->push_back(E->key()); #endif } + + for (Map<StringName, Variant>::Element *E = cd.variant_value.front(); E; E = E->next()) { + + p_constants->push_back(E->key()); + } } -bool Variant::has_numeric_constant(Variant::Type p_type, const StringName &p_value) { +bool Variant::has_constant(Variant::Type p_type, const StringName &p_value) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false); _VariantCall::ConstantData &cd = _VariantCall::constant_data[p_type]; - return cd.value.has(p_value); + return cd.value.has(p_value) || cd.variant_value.has(p_value); } -int Variant::get_numeric_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid) { +Variant Variant::get_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid) { if (r_valid) *r_valid = false; @@ -1389,7 +1404,14 @@ int Variant::get_numeric_constant_value(Variant::Type p_type, const StringName & Map<StringName, int>::Element *E = cd.value.find(p_value); if (!E) { - return -1; + Map<StringName, Variant>::Element *E = cd.variant_value.find(p_value); + if (E) { + if (r_valid) + *r_valid = true; + return E->get(); + } else { + return -1; + } } if (r_valid) *r_valid = true; @@ -1532,6 +1554,7 @@ void register_variant_methods() { ADDFUNC0R(VECTOR2, BOOL, Vector2, is_normalized, varray()); ADDFUNC1R(VECTOR2, REAL, Vector2, distance_to, VECTOR2, "to", varray()); ADDFUNC1R(VECTOR2, REAL, Vector2, distance_squared_to, VECTOR2, "to", varray()); + ADDFUNC1R(VECTOR2, VECTOR2, Vector2, project, VECTOR2, "b", varray()); ADDFUNC1R(VECTOR2, REAL, Vector2, angle_to, VECTOR2, "to", varray()); ADDFUNC1R(VECTOR2, REAL, Vector2, angle_to_point, VECTOR2, "to", varray()); ADDFUNC2R(VECTOR2, VECTOR2, Vector2, linear_interpolate, VECTOR2, "b", REAL, "t", varray()); @@ -1587,6 +1610,7 @@ void register_variant_methods() { ADDFUNC0R(VECTOR3, VECTOR3, Vector3, round, varray()); ADDFUNC1R(VECTOR3, REAL, Vector3, distance_to, VECTOR3, "b", varray()); ADDFUNC1R(VECTOR3, REAL, Vector3, distance_squared_to, VECTOR3, "b", varray()); + ADDFUNC1R(VECTOR3, VECTOR3, Vector3, project, VECTOR3, "b", varray()); ADDFUNC1R(VECTOR3, REAL, Vector3, angle_to, VECTOR3, "to", varray()); ADDFUNC1R(VECTOR3, VECTOR3, Vector3, slide, VECTOR3, "n", varray()); ADDFUNC1R(VECTOR3, VECTOR3, Vector3, bounce, VECTOR3, "n", varray()); @@ -1649,11 +1673,12 @@ void register_variant_methods() { ADDFUNC0NC(DICTIONARY, NIL, Dictionary, clear, varray()); ADDFUNC1R(DICTIONARY, BOOL, Dictionary, has, NIL, "key", varray()); ADDFUNC1R(DICTIONARY, BOOL, Dictionary, has_all, ARRAY, "keys", varray()); - ADDFUNC1(DICTIONARY, NIL, Dictionary, erase, NIL, "key", varray()); + ADDFUNC1R(DICTIONARY, BOOL, Dictionary, erase, NIL, "key", varray()); ADDFUNC0R(DICTIONARY, INT, Dictionary, hash, varray()); ADDFUNC0R(DICTIONARY, ARRAY, Dictionary, keys, varray()); ADDFUNC0R(DICTIONARY, ARRAY, Dictionary, values, varray()); ADDFUNC1R(DICTIONARY, DICTIONARY, Dictionary, duplicate, BOOL, "deep", varray(false)); + ADDFUNC2R(DICTIONARY, NIL, Dictionary, get, NIL, "key", NIL, "default", varray(Variant())); ADDFUNC0R(ARRAY, INT, Array, size, varray()); ADDFUNC0R(ARRAY, BOOL, Array, empty, varray()); @@ -1682,6 +1707,8 @@ void register_variant_methods() { ADDFUNC4R(ARRAY, INT, Array, bsearch_custom, NIL, "value", OBJECT, "obj", STRING, "func", BOOL, "before", varray(true)); ADDFUNC0NC(ARRAY, NIL, Array, invert, varray()); ADDFUNC1R(ARRAY, ARRAY, Array, duplicate, BOOL, "deep", varray(false)); + ADDFUNC0R(ARRAY, NIL, Array, max, varray()); + ADDFUNC0R(ARRAY, NIL, Array, min, varray()); ADDFUNC0R(POOL_BYTE_ARRAY, INT, PoolByteArray, size, varray()); ADDFUNC2(POOL_BYTE_ARRAY, NIL, PoolByteArray, set, INT, "idx", INT, "byte", varray()); @@ -1858,9 +1885,52 @@ void register_variant_methods() { /* REGISTER CONSTANTS */ + _populate_named_colors(); + for (Map<String, Color>::Element *color = _named_colors.front(); color; color = color->next()) { + _VariantCall::add_variant_constant(Variant::COLOR, color->key(), color->value()); + } + _VariantCall::add_constant(Variant::VECTOR3, "AXIS_X", Vector3::AXIS_X); _VariantCall::add_constant(Variant::VECTOR3, "AXIS_Y", Vector3::AXIS_Y); _VariantCall::add_constant(Variant::VECTOR3, "AXIS_Z", Vector3::AXIS_Z); + + _VariantCall::add_variant_constant(Variant::VECTOR3, "ZERO", Vector3(0, 0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "ONE", Vector3(1, 1, 1)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "INF", Vector3(Math_INF, Math_INF, Math_INF)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "LEFT", Vector3(-1, 0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "RIGHT", Vector3(1, 0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "UP", Vector3(0, 1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "DOWN", Vector3(0, -1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "FORWARD", Vector3(0, 0, -1)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "BACK", Vector3(0, 0, 1)); + + _VariantCall::add_variant_constant(Variant::VECTOR2, "ZERO", Vector2(0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "ONE", Vector2(1, 1)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "INF", Vector2(Math_INF, Math_INF)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "LEFT", Vector2(-1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "RIGHT", Vector2(1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "UP", Vector2(0, -1)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "DOWN", Vector2(0, 1)); + + _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "IDENTITY", Transform2D(1, 0, 0, 1, 0, 0)); + _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_X", Transform2D(-1, 0, 0, 1, 0, 0)); + _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_Y", Transform2D(1, 0, 0, -1, 0, 0)); + + Transform identity_transform, transform_x, transform_y, transform_z; + identity_transform.set(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "IDENTITY", identity_transform); + transform_x.set(-1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_X", transform_x); + transform_x.set(1, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_Y", transform_y); + transform_x.set(1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_Z", transform_z); + + _VariantCall::add_variant_constant(Variant::PLANE, "PLANE_YZ", Plane(Vector3(1, 0, 0), 0)); + _VariantCall::add_variant_constant(Variant::PLANE, "PLANE_XZ", Plane(Vector3(0, 1, 0), 0)); + _VariantCall::add_variant_constant(Variant::PLANE, "PLANE_XY", Plane(Vector3(0, 0, 1), 0)); + + _VariantCall::add_variant_constant(Variant::QUAT, "IDENTITY", Quat(0, 0, 0, 1)); } void unregister_variant_methods() { diff --git a/core/variant_op.cpp b/core/variant_op.cpp index bfa69b1fde..9f172f0d57 100644 --- a/core/variant_op.cpp +++ b/core/variant_op.cpp @@ -30,9 +30,9 @@ #include "variant.h" -#include "core_string_names.h" -#include "object.h" -#include "script_language.h" +#include "core/core_string_names.h" +#include "core/object.h" +#include "core/script_language.h" #define CASE_TYPE_ALL(PREFIX, OP) \ CASE_TYPE(PREFIX, OP, INT) \ @@ -521,7 +521,7 @@ void Variant::evaluate(const Operator &p_op, const Variant &p_a, const Dictionary *arr_a = reinterpret_cast<const Dictionary *>(p_a._data._mem); const Dictionary *arr_b = reinterpret_cast<const Dictionary *>(p_b._data._mem); - _RETURN((*arr_a == *arr_b) == false); + _RETURN(*arr_a != *arr_b); } CASE_TYPE(math, OP_NOT_EQUAL, ARRAY) { @@ -1656,13 +1656,13 @@ Variant Variant::get_named(const StringName &p_index, bool *r_valid) const { } else if (p_index == CoreStringNames::singleton->a) { return v->a; } else if (p_index == CoreStringNames::singleton->r8) { - return int(v->r * 255.0); + return int(Math::round(v->r * 255.0)); } else if (p_index == CoreStringNames::singleton->g8) { - return int(v->g * 255.0); + return int(Math::round(v->g * 255.0)); } else if (p_index == CoreStringNames::singleton->b8) { - return int(v->b * 255.0); + return int(Math::round(v->b * 255.0)); } else if (p_index == CoreStringNames::singleton->a8) { - return int(v->a * 255.0); + return int(Math::round(v->a * 255.0)); } else if (p_index == CoreStringNames::singleton->h) { return v->get_h(); } else if (p_index == CoreStringNames::singleton->s) { @@ -2149,7 +2149,7 @@ void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid) int idx = p_index; if (idx < 0) idx += 4; - if (idx >= 0 || idx < 4) { + if (idx >= 0 && idx < 4) { Color *v = reinterpret_cast<Color *>(_data._mem); (*v)[idx] = p_value; valid = true; @@ -2524,7 +2524,7 @@ Variant Variant::get(const Variant &p_index, bool *r_valid) const { int idx = p_index; if (idx < 0) idx += 4; - if (idx >= 0 || idx < 4) { + if (idx >= 0 && idx < 4) { const Color *v = reinterpret_cast<const Color *>(_data._mem); valid = true; return (*v)[idx]; @@ -3521,7 +3521,7 @@ void Variant::interpolate(const Variant &a, const Variant &b, float c, Variant & //not as efficient but.. real_t va = a; real_t vb = b; - r_dst = (1.0 - c) * va + vb * c; + r_dst = va + (vb - va) * c; } else { r_dst = a; @@ -3542,13 +3542,13 @@ void Variant::interpolate(const Variant &a, const Variant &b, float c, Variant & case INT: { int64_t va = a._data._int; int64_t vb = b._data._int; - r_dst = int((1.0 - c) * va + vb * c); + r_dst = int(va + (vb - va) * c); } return; case REAL: { real_t va = a._data._real; real_t vb = b._data._real; - r_dst = (1.0 - c) * va + vb * c; + r_dst = va + (vb - va) * c; } return; case STRING: { @@ -3556,7 +3556,9 @@ void Variant::interpolate(const Variant &a, const Variant &b, float c, Variant & String sa = *reinterpret_cast<const String *>(a._data._mem); String sb = *reinterpret_cast<const String *>(b._data._mem); String dst; - int csize = sb.length() * c + sa.length() * (1.0 - c); + int sa_len = sa.length(); + int sb_len = sb.length(); + int csize = sa_len + (sb_len - sa_len) * c; if (csize == 0) { r_dst = ""; return; diff --git a/core/variant_parser.cpp b/core/variant_parser.cpp index 446aee286d..3d5ae74e92 100644 --- a/core/variant_parser.cpp +++ b/core/variant_parser.cpp @@ -30,10 +30,10 @@ #include "variant_parser.h" +#include "core/io/resource_loader.h" +#include "core/os/input_event.h" +#include "core/os/keyboard.h" #include "core/string_buffer.h" -#include "io/resource_loader.h" -#include "os/input_event.h" -#include "os/keyboard.h" CharType VariantParser::StreamFile::get_char() { @@ -1429,10 +1429,10 @@ Error VariantParser::_parse_tag(Token &token, Stream *p_stream, int &line, Strin break; if (parsing_tag && token.type == TK_PERIOD) { - r_tag.name += "."; //support tags such as [someprop.Anroid] for specific platforms + r_tag.name += "."; //support tags such as [someprop.Android] for specific platforms get_token(p_stream, token, line, r_err_str); } else if (parsing_tag && token.type == TK_COLON) { - r_tag.name += ":"; //support tags such as [someprop.Anroid] for specific platforms + r_tag.name += ":"; //support tags such as [someprop.Android] for specific platforms get_token(p_stream, token, line, r_err_str); } else { parsing_tag = false; diff --git a/core/variant_parser.h b/core/variant_parser.h index 8d95595234..531c1d59cd 100644 --- a/core/variant_parser.h +++ b/core/variant_parser.h @@ -31,9 +31,9 @@ #ifndef VARIANT_PARSER_H #define VARIANT_PARSER_H -#include "os/file_access.h" -#include "resource.h" -#include "variant.h" +#include "core/os/file_access.h" +#include "core/resource.h" +#include "core/variant.h" class VariantParser { public: diff --git a/core/vector.h b/core/vector.h index 7e3da34be0..38a1082407 100644 --- a/core/vector.h +++ b/core/vector.h @@ -36,25 +36,25 @@ * @author Juan Linietsky * Vector container. Regular Vector Container. Use with care and for smaller arrays when possible. Use PoolVector for large arrays. */ -#include "cowdata.h" -#include "error_macros.h" -#include "os/memory.h" -#include "sort.h" + +#include "core/cowdata.h" +#include "core/error_macros.h" +#include "core/os/memory.h" +#include "core/sort.h" template <class T> class VectorWriteProxy { friend class Vector<T>; - Vector<T> &_parent; + CowData<T> *_parent; - _FORCE_INLINE_ VectorWriteProxy(Vector<T> &parent) : + _FORCE_INLINE_ VectorWriteProxy(CowData<T> *parent) : _parent(parent){}; - VectorWriteProxy(const VectorWriteProxy<T> &p_other); public: _FORCE_INLINE_ T &operator[](int p_index) { - CRASH_BAD_INDEX(p_index, _parent.size()); + CRASH_BAD_INDEX(p_index, _parent->size()); - return _parent.ptrw()[p_index]; + return _parent->ptrw()[p_index]; } }; @@ -62,39 +62,39 @@ template <class T> class Vector { friend class VectorWriteProxy<T>; - CowData<T> _cowdata; + CowData<T> *_cowdata; public: VectorWriteProxy<T> write; bool push_back(const T &p_elem); - void remove(int p_index) { _cowdata.remove(p_index); } + void remove(int p_index) { _cowdata->remove(p_index); } void erase(const T &p_val) { int idx = find(p_val); if (idx >= 0) remove(idx); }; void invert(); - _FORCE_INLINE_ T *ptrw() { return _cowdata.ptrw(); } - _FORCE_INLINE_ const T *ptr() const { return _cowdata.ptr(); } + _FORCE_INLINE_ T *ptrw() { return _cowdata->ptrw(); } + _FORCE_INLINE_ const T *ptr() const { return _cowdata->ptr(); } _FORCE_INLINE_ void clear() { resize(0); } - _FORCE_INLINE_ bool empty() const { return _cowdata.empty(); } + _FORCE_INLINE_ bool empty() const { return _cowdata->empty(); } - _FORCE_INLINE_ T get(int p_index) { return _cowdata.get(p_index); } - _FORCE_INLINE_ const T get(int p_index) const { return _cowdata.get(p_index); } - _FORCE_INLINE_ void set(int p_index, const T &p_elem) { _cowdata.set(p_index, p_elem); } - _FORCE_INLINE_ int size() const { return _cowdata.size(); } - Error resize(int p_size) { return _cowdata.resize(p_size); } - _FORCE_INLINE_ const T &operator[](int p_index) const { return _cowdata.get(p_index); } - Error insert(int p_pos, const T &p_val) { return _cowdata.insert(p_pos, p_val); } + _FORCE_INLINE_ T get(int p_index) { return _cowdata->get(p_index); } + _FORCE_INLINE_ const T get(int p_index) const { return _cowdata->get(p_index); } + _FORCE_INLINE_ void set(int p_index, const T &p_elem) { _cowdata->set(p_index, p_elem); } + _FORCE_INLINE_ int size() const { return _cowdata->size(); } + Error resize(int p_size) { return _cowdata->resize(p_size); } + _FORCE_INLINE_ const T &operator[](int p_index) const { return _cowdata->get(p_index); } + Error insert(int p_pos, const T &p_val) { return _cowdata->insert(p_pos, p_val); } void append_array(const Vector<T> &p_other); template <class C> void sort_custom() { - int len = _cowdata.size(); + int len = _cowdata->size(); if (len == 0) return; @@ -110,7 +110,7 @@ public: void ordered_insert(const T &p_val) { int i; - for (i = 0; i < _cowdata.size(); i++) { + for (i = 0; i < _cowdata->size(); i++) { if (p_val < operator[](i)) { break; @@ -136,13 +136,19 @@ public: } _FORCE_INLINE_ Vector() : - write(VectorWriteProxy<T>(*this)) {} + _cowdata(new CowData<T>()), + write(VectorWriteProxy<T>(_cowdata)) {} _FORCE_INLINE_ Vector(const Vector &p_from) : - write(VectorWriteProxy<T>(*this)) { _cowdata._ref(p_from._cowdata); } + _cowdata(new CowData<T>()), + write(VectorWriteProxy<T>(_cowdata)) { _cowdata->_ref(p_from._cowdata); } inline Vector &operator=(const Vector &p_from) { - _cowdata._ref(p_from._cowdata); + _cowdata->_ref(p_from._cowdata); return *this; } + + _FORCE_INLINE_ ~Vector() { + delete _cowdata; + } }; template <class T> diff --git a/core/version.h b/core/version.h index d39172865a..2a3a282d82 100644 --- a/core/version.h +++ b/core/version.h @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "version_generated.gen.h" +#include "core/version_generated.gen.h" // Godot versions are of the form <major>.<minor> for the initial release, // and then <major>.<minor>.<patch> for subsequent bugfix releases where <patch> != 0 diff --git a/core/vmap.h b/core/vmap.h index ce0ddc4ec6..5f6d8190c6 100644 --- a/core/vmap.h +++ b/core/vmap.h @@ -31,27 +31,28 @@ #ifndef VMAP_H #define VMAP_H -#include "cowdata.h" -#include "typedefs.h" +#include "core/cowdata.h" +#include "core/typedefs.h" template <class T, class V> class VMap { - - struct _Pair { +public: + struct Pair { T key; V value; - _FORCE_INLINE_ _Pair() {} + _FORCE_INLINE_ Pair() {} - _FORCE_INLINE_ _Pair(const T &p_key, const V &p_value) { + _FORCE_INLINE_ Pair(const T &p_key, const V &p_value) { key = p_key; value = p_value; } }; - CowData<_Pair> _cowdata; +private: + CowData<Pair> _cowdata; _FORCE_INLINE_ int _find(const T &p_val, bool &r_exact) const { @@ -61,7 +62,7 @@ class VMap { int low = 0; int high = _cowdata.size() - 1; - const _Pair *a = _cowdata.ptr(); + const Pair *a = _cowdata.ptr(); int middle = 0; #if DEBUG_ENABLED @@ -95,7 +96,7 @@ class VMap { int low = 0; int high = _cowdata.size() - 1; int middle; - const _Pair *a = _cowdata.ptr(); + const Pair *a = _cowdata.ptr(); while (low <= high) { middle = (low + high) / 2; @@ -121,7 +122,7 @@ public: _cowdata.get_m(pos).value = p_val; return pos; } - _cowdata.insert(pos, _Pair(p_key, p_val)); + _cowdata.insert(pos, Pair(p_key, p_val)); return pos; } @@ -152,12 +153,12 @@ public: _FORCE_INLINE_ int size() const { return _cowdata.size(); } _FORCE_INLINE_ bool empty() const { return _cowdata.empty(); } - const _Pair *get_array() const { + const Pair *get_array() const { return _cowdata.ptr(); } - _Pair *get_array() { + Pair *get_array() { return _cowdata.ptrw(); } diff --git a/core/vset.h b/core/vset.h index 7f4d8e7f62..9ec329b45f 100644 --- a/core/vset.h +++ b/core/vset.h @@ -31,8 +31,8 @@ #ifndef VSET_H #define VSET_H -#include "typedefs.h" -#include "vector.h" +#include "core/typedefs.h" +#include "core/vector.h" template <class T> class VSet { |