diff options
364 files changed, 11337 insertions, 2382 deletions
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index cf08225a40..894cd1a53a 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -278,6 +278,11 @@ Comment: curl Copyright: 1998-2012, Daniel Stenberg et al. License: curl +Files: ./thirdparty/misc/easing_equations.cpp +Comment: Robert Penner's Easing Functions +Copyright: 2001, Robert Penner +License: BSD-3-clause + Files: ./thirdparty/misc/fastlz.c ./thirdparty/misc/fastlz.h Comment: FastLZ diff --git a/SConstruct b/SConstruct index 1c55e0be93..a056a0c3ae 100644 --- a/SConstruct +++ b/SConstruct @@ -123,6 +123,7 @@ env_base.__class__.add_shared_library = methods.add_shared_library env_base.__class__.add_library = methods.add_library env_base.__class__.add_program = methods.add_program env_base.__class__.CommandNoCache = methods.CommandNoCache +env_base.__class__.disable_warnings = methods.disable_warnings env_base["x86_libtheora_opt_gcc"] = False env_base["x86_libtheora_opt_vc"] = False @@ -156,7 +157,6 @@ opts.Add(BoolVariable('deprecated', "Enable deprecated features", True)) opts.Add(BoolVariable('gdscript', "Enable GDScript support", True)) opts.Add(BoolVariable('minizip', "Enable ZIP archive support using minizip", True)) opts.Add(BoolVariable('xaudio2', "Enable the XAudio2 audio driver", False)) -opts.Add(BoolVariable('xml', "Enable XML format support for resources", True)) # Advanced options opts.Add(BoolVariable('disable_3d', "Disable 3D nodes for a smaller executable", False)) @@ -190,6 +190,7 @@ opts.Add(BoolVariable('builtin_pcre2', "Use the built-in PCRE2 library)", True)) opts.Add(BoolVariable('builtin_recast', "Use the built-in Recast library", True)) opts.Add(BoolVariable('builtin_squish', "Use the built-in squish library", True)) opts.Add(BoolVariable('builtin_thekla_atlas', "Use the built-in thekla_altas library", True)) +opts.Add(BoolVariable('builtin_xatlas', "Use the built-in xatlas library", True)) opts.Add(BoolVariable('builtin_zlib', "Use the built-in zlib library", True)) opts.Add(BoolVariable('builtin_zstd', "Use the built-in Zstd library", True)) @@ -226,14 +227,14 @@ Help(opts.GenerateHelpText(env_base)) # generate help # add default include paths -env_base.Append(CPPPATH=['#editor', '#drivers', '#']) +env_base.Append(CPPPATH=['#editor', '#']) # configure ENV for platform env_base.platform_exporters = platform_exporters env_base.platform_apis = platform_apis if (env_base['target'] == 'debug'): - env_base.Append(CPPDEFINES=['DEBUG_MEMORY_ALLOC', 'SCI_NAMESPACE']) + env_base.Append(CPPDEFINES=['DEBUG_MEMORY_ALLOC']) if (env_base['no_editor_splash']): env_base.Append(CPPDEFINES=['NO_EDITOR_SPLASH']) @@ -333,12 +334,13 @@ if selected_platform in platform_list: # Set exception handling model to avoid warnings caused by Windows system headers. env.Append(CCFLAGS=['/EHsc']) else: # Rest of the world + disable_nonessential_warnings = ['-Wno-sign-compare'] if (env["warnings"] == 'extra'): env.Append(CCFLAGS=['-Wall', '-Wextra']) elif (env["warnings"] == 'all' or env["warnings"] == 'yes'): - env.Append(CCFLAGS=['-Wall']) + env.Append(CCFLAGS=['-Wall'] + disable_nonessential_warnings) elif (env["warnings"] == 'moderate'): - env.Append(CCFLAGS=['-Wall', '-Wno-unused']) + env.Append(CCFLAGS=['-Wall', '-Wno-unused'] + disable_nonessential_warnings) else: # 'no' env.Append(CCFLAGS=['-w']) env.Append(CCFLAGS=['-Werror=return-type']) @@ -441,8 +443,6 @@ if selected_platform in platform_list: env.Append(CPPDEFINES=['ADVANCED_GUI_DISABLED']) if env['minizip']: env.Append(CPPDEFINES=['MINIZIP_ENABLED']) - if env['xml']: - env.Append(CPPDEFINES=['XML_ENABLED']) if not env['verbose']: methods.no_verbose(sys, env) diff --git a/core/SCsub b/core/SCsub index 17b6e2c7ea..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 = "" @@ -55,10 +54,14 @@ with open("script_encryption_key.gen.cpp", "w") as f: f.write("#include \"core/project_settings.h\"\nuint8_t script_encryption_key[32]={" + txt + "};\n") -# Add required thirdparty code. Header paths are hardcoded, we don't need to append +# Add required thirdparty code. +env_thirdparty = env.Clone() +env_thirdparty.disable_warnings() + +# 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,10 +111,42 @@ 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 @@ -122,4 +181,3 @@ SConscript('bind/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/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/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/file_access_network.h b/core/io/file_access_network.h index 7e4669ffd5..e32dcea990 100644 --- a/core/io/file_access_network.h +++ b/core/io/file_access_network.h @@ -47,8 +47,6 @@ class FileAccessNetworkClient { int size; }; - int ml; - List<BlockRequest> block_requests; Semaphore *sem; diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h index 4fe0651a55..9bb1ad221a 100644 --- a/core/io/file_access_zip.h +++ b/core/io/file_access_zip.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 ac563df0c3..36dd688e77 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -374,6 +374,7 @@ Error HTTPClient::poll() { } break; } } break; + case STATUS_BODY: case STATUS_CONNECTED: { // Check if we are still connected if (ssl) { @@ -480,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: { diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index e15519da47..628019ef7f 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -807,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; @@ -824,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) { @@ -850,7 +851,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 (flags & ENCODE_FLAG_64) { //64 bits if (buf) { encode_uint64(val, buf); @@ -869,7 +870,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo 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/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 02c2c6ce1a..e5741014a4 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -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); } diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h index 35d594f228..513252055f 100644 --- a/core/io/resource_format_binary.h +++ b/core/io/resource_format_binary.h @@ -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/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 c3314a8990..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 "core/os/copymem.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/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/expression.cpp b/core/math/expression.cpp index c0d7f874d2..a16267cf0a 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -756,6 +756,10 @@ void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant //////// +static bool _is_number(CharType c) { + return (c >= '0' && c <= '9'); +} + Error Expression::_get_token(Token &r_token) { while (true) { @@ -813,17 +817,12 @@ Error Expression::_get_token(Token &r_token) { r_token.type = TK_COLON; return OK; }; - case '.': { - - r_token.type = TK_PERIOD; - return OK; - }; case '$': { r_token.type = TK_INPUT; int index = 0; do { - if (expression[str_ofs] < '0' || expression[str_ofs] > '9') { + if (!_is_number(expression[str_ofs])) { _set_error("Expected number after '$'"); r_token.type = TK_ERROR; return ERR_PARSE_ERROR; @@ -832,7 +831,7 @@ Error Expression::_get_token(Token &r_token) { index += expression[str_ofs] - '0'; str_ofs++; - } while (expression[str_ofs] >= '0' && expression[str_ofs] <= '9'); + } while (_is_number(expression[str_ofs])); r_token.value = index; return OK; @@ -979,14 +978,14 @@ Error Expression::_get_token(Token &r_token) { r_token.type = TK_ERROR; return ERR_PARSE_ERROR; } - if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { + 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 (c >= '0' && c <= '9') { + if (_is_number(c)) { v = c - '0'; } else if (c >= 'a' && c <= 'f') { v = c - 'a'; @@ -1032,7 +1031,8 @@ Error Expression::_get_token(Token &r_token) { break; } - if (cchar >= '0' && cchar <= '9') { + CharType next_char = (str_ofs >= expression.length()) ? 0 : expression[str_ofs]; + if (_is_number(cchar) || (cchar == '.' && _is_number(next_char))) { //a number String num; @@ -1053,7 +1053,7 @@ Error Expression::_get_token(Token &r_token) { switch (reading) { case READING_INT: { - if (c >= '0' && c <= '9') { + if (_is_number(c)) { //pass } else if (c == '.') { reading = READING_DEC; @@ -1067,7 +1067,7 @@ Error Expression::_get_token(Token &r_token) { } break; case READING_DEC: { - if (c >= '0' && c <= '9') { + if (_is_number(c)) { } else if (c == 'e') { reading = READING_EXP; @@ -1079,7 +1079,7 @@ Error Expression::_get_token(Token &r_token) { } break; case READING_EXP: { - if (c >= '0' && c <= '9') { + if (_is_number(c)) { exp_beg = true; } else if ((c == '-' || c == '+') && !exp_sign && !exp_beg) { @@ -1114,7 +1114,7 @@ Error Expression::_get_token(Token &r_token) { String id; bool first = true; - while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && cchar >= '0' && cchar <= '9')) { + while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && _is_number(cchar))) { id += String::chr(cchar); cchar = GET_CHAR(); @@ -1176,6 +1176,12 @@ Error Expression::_get_token(Token &r_token) { } 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; @@ -1183,6 +1189,7 @@ Error Expression::_get_token(Token &r_token) { } } } +#undef GET_CHAR } r_token.type = TK_ERROR; diff --git a/core/message_queue.h b/core/message_queue.h index f51da3c7a3..2083bb0639 100644 --- a/core/message_queue.h +++ b/core/message_queue.h @@ -32,7 +32,6 @@ #define MESSAGE_QUEUE_H #include "core/object.h" -#include "core/os/mutex.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/node_path.cpp b/core/node_path.cpp index 35d6fdcf10..91e2aa5f4e 100644 --- a/core/node_path.cpp +++ b/core/node_path.cpp @@ -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/oa_hash_map.h b/core/oa_hash_map.h index a2d76381ca..3705762d6c 100644 --- a/core/oa_hash_map.h +++ b/core/oa_hash_map.h @@ -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; } } diff --git a/core/object.cpp b/core/object.cpp index 96914fe141..946040ba34 100644 --- a/core/object.cpp +++ b/core/object.cpp @@ -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) { } @@ -1254,7 +1254,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 { @@ -2014,11 +2017,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) { @@ -2095,6 +2100,5 @@ void ObjectDB::cleanup() { instances.clear(); instance_checks.clear(); rw_lock->write_unlock(); - memdelete(rw_lock); } diff --git a/core/object.h b/core/object.h index be405a47a6..b23160c1df 100644 --- a/core/object.h +++ b/core/object.h @@ -116,6 +116,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, @@ -187,11 +188,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); } @@ -317,7 +318,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 { \ @@ -327,7 +328,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) { \ @@ -337,7 +338,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 { \ @@ -356,7 +357,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) { \ @@ -421,7 +422,7 @@ private: }; #ifdef DEBUG_ENABLED - friend class _ObjectDebugLock; + friend struct _ObjectDebugLock; #endif friend bool predelete_handler(Object *); friend void postinitialize_handler(Object *); @@ -513,16 +514,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); 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/dir_access.cpp b/core/os/dir_access.cpp index dbd62cb3bb..daa3eacd5f 100644 --- a/core/os/dir_access.cpp +++ b/core/os/dir_access.cpp @@ -227,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; diff --git a/core/os/file_access.cpp b/core/os/file_access.cpp index 224dea3343..e09e5e16ad 100644 --- a/core/os/file_access.cpp +++ b/core/os/file_access.cpp @@ -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; diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp index fe1dc2cdd1..388e3b77a3 100644 --- a/core/script_debugger_remote.cpp +++ b/core/script_debugger_remote.cpp @@ -98,36 +98,6 @@ Error ScriptDebuggerRemote::connect_to_host(const String &p_host, uint16_t p_por 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); @@ -1094,12 +1064,12 @@ ScriptDebuggerRemote::ScriptDebuggerRemote() : 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), diff --git a/core/typedefs.h b/core/typedefs.h index 76778429b0..2b26bf08f7 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -105,11 +105,11 @@ T *_nullptr() { /** 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 diff --git a/core/variant.h b/core/variant.h index 2fffb31de6..8953217760 100644 --- a/core/variant.h +++ b/core/variant.h @@ -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. diff --git a/doc/classes/@GDScript.xml b/doc/classes/@GDScript.xml index 7bd332a3e4..493f55e89b 100644 --- a/doc/classes/@GDScript.xml +++ b/doc/classes/@GDScript.xml @@ -55,7 +55,7 @@ <argument index="0" name="s" type="float"> </argument> <description> - Returns the absolute value of parameter [code]s[/code] (i.e. unsigned value, works for integer and float). + Returns the absolute value of parameter [code]s[/code] (i.e. unsigned value, works for integer and float). [codeblock] # a is 1 a = abs(-1) @@ -373,7 +373,7 @@ <description> Returns the floating-point remainder of [code]x/y[/code] that wraps equally in positive and negative. [codeblock] - var i = -10; + var i = -10 while i < 0: prints(i, fposmod(i, 10)) i += 1 diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 5a53e7cb05..65d339c0d5 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -886,8 +886,10 @@ Middle Mouse Button </constant> <constant name="BUTTON_XBUTTON1" value="8" enum="ButtonList"> + Extra Mouse Button 1 </constant> <constant name="BUTTON_XBUTTON2" value="9" enum="ButtonList"> + Extra Mouse Button 2 </constant> <constant name="BUTTON_WHEEL_UP" value="4" enum="ButtonList"> Mouse wheel up @@ -911,8 +913,10 @@ Middle Mouse Button Mask </constant> <constant name="BUTTON_MASK_XBUTTON1" value="128" enum="ButtonList"> + Extra Mouse Button 1 Mask </constant> <constant name="BUTTON_MASK_XBUTTON2" value="256" enum="ButtonList"> + Extra Mouse Button 2 Mask </constant> <constant name="JOY_BUTTON_0" value="0" enum="JoystickList"> Joypad Button 0 diff --git a/doc/classes/ARVRController.xml b/doc/classes/ARVRController.xml index 9c306c3ea4..d3d6fce537 100644 --- a/doc/classes/ARVRController.xml +++ b/doc/classes/ARVRController.xml @@ -6,7 +6,7 @@ <description> This is a helper spatial node that is linked to the tracking of controllers. It also offers several handy pass throughs to the state of buttons and such on the controllers. Controllers are linked by their id. You can create controller nodes before the controllers are available. Say your game always uses two controllers (one for each hand) you can predefine the controllers with id 1 and 2 and they will become active as soon as the controllers are identified. If you expect additional controllers to be used you should react to the signals and add ARVRController nodes to your scene. - The position of the controller node is automatically updated by the ARVR Server. This makes this node ideal to add child nodes to visualise the controller. + The position of the controller node is automatically updated by the ARVR Server. This makes this node ideal to add child nodes to visualise the controller. </description> <tutorials> </tutorials> diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index b134650f8d..b013b3c4ae 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -120,7 +120,7 @@ <argument index="3" name="before" type="bool" default="True"> </argument> <description> - Finds the index of an existing value (or the insertion index that maintains sorting order, if the value is not yet present in the array) using binary search and a custom comparison method. Optionally, a before specifier can be passed. If false, the returned index comes after all existing entries of the value in the array. The custom method receives two arguments (an element from the array and the value searched for) and must return true if the first argument is less than the second, and return false otherwise. Note that calling bsearch on an unsorted array results in unexpected behavior. + Finds the index of an existing value (or the insertion index that maintains sorting order, if the value is not yet present in the array) using binary search and a custom comparison method. Optionally, a before specifier can be passed. If false, the returned index comes after all existing entries of the value in the array. The custom method receives two arguments (an element from the array and the value searched for) and must return true if the first argument is less than the second, and return false otherwise. Note that calling bsearch on an unsorted array results in unexpected behavior. </description> </method> <method name="clear"> diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml index 453f28fe5a..ed3d2d2205 100644 --- a/doc/classes/ArrayMesh.xml +++ b/doc/classes/ArrayMesh.xml @@ -3,6 +3,23 @@ <brief_description> </brief_description> <description> + The [code]ArrayMesh[/code] is used to construct a [Mesh] by specifying the attributes as arrays. The most basic example is the creation of a single triangle + [codeblock] + var vertices = PoolVector3Array() + vertices.push_back(Vector3(0,1,0)) + vertices.push_back(Vector3(1,0,0)) + vertices.push_back(Vector3(0,0,1)) + # Initialize the ArrayMesh. + var arr_mesh = ArrayMesh.new() + var arrays = [] + arrays.resize(ArrayMesh.ARRAY_MAX) + arrays[ArrayMesh.ARRAY_VERTEX] = vertices + # Create the Mesh. + arr_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays) + var m = MeshInstance.new() + m.mesh = arr_mesh + [/codeblock] + The [code]MeshInstance[/code] is ready to be added to the SceneTree to be shown. </description> <tutorials> </tutorials> diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml index b66239181a..2e3cc2e5d1 100644 --- a/doc/classes/Color.xml +++ b/doc/classes/Color.xml @@ -4,7 +4,7 @@ Color in RGBA format with some support for ARGB format. </brief_description> <description> - A color is represented as red, green and blue (r,g,b) components. Additionally, "a" represents the alpha component, often used for transparency. Values are in floating point and usually range from 0 to 1. Some methods (such as set_modulate(color)) may accept values > 1. + A color is represented as red, green and blue (r,g,b) components. Additionally, "a" represents the alpha component, often used for transparency. Values are in floating point and usually range from 0 to 1. Some methods (such as set_modulate(color)) may accept values > 1. You can also create a color from standardised color names with Color.ColorN (e.g. Color.green) or [method @GDScript.ColorN]. </description> <tutorials> diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index 05b2c2704e..1a27aea23f 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -229,9 +229,9 @@ extends Control func get_drag_data(position): - var mydata = make_data() - set_drag_preview(make_preview(mydata)) - return mydata + var mydata = make_data() + set_drag_preview(make_preview(mydata)) + return mydata [/codeblock] </description> </method> diff --git a/doc/classes/Curve2D.xml b/doc/classes/Curve2D.xml index 26de8be42c..ab9b27542c 100644 --- a/doc/classes/Curve2D.xml +++ b/doc/classes/Curve2D.xml @@ -111,7 +111,7 @@ <argument index="1" name="t" type="float"> </argument> <description> - Returns the position between the vertex "idx" and the vertex "idx"+1, where "t" controls if the point is the first vertex (t = 0.0), the last vertex (t = 1.0), or in between. Values of "t" outside the range (0.0 >= t <=1) give strange, but predictable results. + Returns the position between the vertex "idx" and the vertex "idx"+1, where "t" controls if the point is the first vertex (t = 0.0), the last vertex (t = 1.0), or in between. Values of "t" outside the range (0.0 >= t <=1) give strange, but predictable results. If "idx" is out of bounds it is truncated to the first or last vertex, and "t" is ignored. If the curve has no points, the function sends an error to the console, and returns (0, 0). </description> </method> diff --git a/doc/classes/Curve3D.xml b/doc/classes/Curve3D.xml index 1355c74faf..c3ee309f0b 100644 --- a/doc/classes/Curve3D.xml +++ b/doc/classes/Curve3D.xml @@ -135,7 +135,7 @@ <argument index="1" name="t" type="float"> </argument> <description> - Returns the position between the vertex "idx" and the vertex "idx"+1, where "t" controls if the point is the first vertex (t = 0.0), the last vertex (t = 1.0), or in between. Values of "t" outside the range (0.0 >= t <=1) give strange, but predictable results. + Returns the position between the vertex "idx" and the vertex "idx"+1, where "t" controls if the point is the first vertex (t = 0.0), the last vertex (t = 1.0), or in between. Values of "t" outside the range (0.0 >= t <=1) give strange, but predictable results. If "idx" is out of bounds it is truncated to the first or last vertex, and "t" is ignored. If the curve has no points, the function sends an error to the console, and returns (0, 0, 0). </description> </method> diff --git a/doc/classes/EditorImportPlugin.xml b/doc/classes/EditorImportPlugin.xml index fdc204605e..aaba30ccc9 100644 --- a/doc/classes/EditorImportPlugin.xml +++ b/doc/classes/EditorImportPlugin.xml @@ -41,7 +41,7 @@ return FAILED var mesh = Mesh.new() - # Fill the Mesh with data read in 'file', left as exercise to the reader + # Fill the Mesh with data read in 'file', left as exercise to the reader var filename = save_path + "." + get_save_extension() ResourceSaver.save(filename, mesh) diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index d9929b3d31..a0d6d29be6 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -95,6 +95,7 @@ <argument index="0" name="axis" type="String"> </argument> <description> + Returns the index of the provided axis name. </description> </method> <method name="get_joy_axis_string"> @@ -103,6 +104,7 @@ <argument index="0" name="axis_index" type="int"> </argument> <description> + Receives a [code]JOY_AXIS_*[/code] Enum and returns its equivalent name as a string. </description> </method> <method name="get_joy_button_index_from_string"> @@ -111,6 +113,7 @@ <argument index="0" name="button" type="String"> </argument> <description> + Returns the index of the provided button name. </description> </method> <method name="get_joy_button_string"> @@ -119,6 +122,7 @@ <argument index="0" name="button_index" type="int"> </argument> <description> + Receives a [code]JOY_BUTTON_*[/code] Enum and returns it's equivalent name as a string. </description> </method> <method name="get_joy_guid" qualifiers="const"> diff --git a/doc/classes/InputEventMouseButton.xml b/doc/classes/InputEventMouseButton.xml index 50641dceed..a3a9055087 100644 --- a/doc/classes/InputEventMouseButton.xml +++ b/doc/classes/InputEventMouseButton.xml @@ -18,7 +18,7 @@ Mouse button identifier, one of the BUTTON_* or BUTTON_WHEEL_* constants in [@GlobalScope]. </member> <member name="doubleclick" type="bool" setter="set_doubleclick" getter="is_doubleclick"> - If [code]true[/code] the mouse button's state is a double-click. If [code]false[/code] the mouse button's state is released. + If [code]true[/code] the mouse button's state is a double-click. </member> <member name="factor" type="float" setter="set_factor" getter="get_factor"> Magnitude. Amount (or delta) of the event. Used for scroll events, indicates scroll amount (vertically or horizontally). Only supported on some platforms, sensitivity varies by platform. May be 0 if not supported. diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml index 4723cf8ee4..38d32fe7c8 100644 --- a/doc/classes/ItemList.xml +++ b/doc/classes/ItemList.xml @@ -33,7 +33,7 @@ <argument index="2" name="selectable" type="bool" default="true"> </argument> <description> - Adds an item to the item list with specified text. Specify an icon of null for a list item with no icon. + Adds an item to the item list with specified text. Specify an icon of null for a list item with no icon. If selectable is true the list item will be selectable. </description> </method> @@ -222,7 +222,7 @@ </argument> <description> Select the item at the specified index. - Note: This method does not trigger the item selection signal. + Note: This method does not trigger the item selection signal. </description> </method> <method name="set_item_custom_bg_color"> diff --git a/doc/classes/LargeTexture.xml b/doc/classes/LargeTexture.xml index 763b38f49e..9526604e6d 100644 --- a/doc/classes/LargeTexture.xml +++ b/doc/classes/LargeTexture.xml @@ -74,7 +74,7 @@ <argument index="1" name="texture" type="Texture"> </argument> <description> - Sets the [Texture] of the piece with index "idx" to "ofs". + Sets the [Texture] of the piece with index "idx" to "texture". </description> </method> <method name="set_size"> diff --git a/doc/classes/NetworkedMultiplayerPeer.xml b/doc/classes/NetworkedMultiplayerPeer.xml index e878b3a746..ea6fe6d11c 100644 --- a/doc/classes/NetworkedMultiplayerPeer.xml +++ b/doc/classes/NetworkedMultiplayerPeer.xml @@ -92,13 +92,13 @@ </signals> <constants> <constant name="TRANSFER_MODE_UNRELIABLE" value="0" enum="TransferMode"> - Packets are sent via unordered UDP packets. + Packets are not acknowledged, no resend attempts are made for lost packets. Packets may arrive in any order. Potentially faster than [code]TRANSFER_MODE_UNRELIABLE_ORDERED[/code]. Use for non-critical data, and always consider whether the order matters. </constant> <constant name="TRANSFER_MODE_UNRELIABLE_ORDERED" value="1" enum="TransferMode"> - Packets are sent via ordered UDP packets. + Packets are not acknowledged, no resend attempts are made for lost packets. Packets are received in the order they were sent in. Potentially faster than [code]TRANSFER_MODE_RELIABLE[/code]. Use for non-critical data or data that would be outdated if received late due to resend attempt(s) anyway, for example movement and positional data. </constant> <constant name="TRANSFER_MODE_RELIABLE" value="2" enum="TransferMode"> - Packets are sent via TCP packets. + Packets must be received and resend attempts should be made until the packets are acknowledged. Packets must be received in the order they were sent in. Most reliable transfer mode, but potentially slowest due to the overhead. Use for critical data that must be transmitted and arrive in order, for example an ability being triggered or a chat message. Consider carefully if the information really is critical, and use sparingly. </constant> <constant name="CONNECTION_DISCONNECTED" value="0" enum="ConnectionStatus"> The ongoing connection disconnected. diff --git a/doc/classes/PackedScene.xml b/doc/classes/PackedScene.xml index 8d810bc9c4..0d58e61c3a 100644 --- a/doc/classes/PackedScene.xml +++ b/doc/classes/PackedScene.xml @@ -11,7 +11,7 @@ var scene = PackedScene.new() var result = scene.pack(child) if result == OK: - ResourceSaver.save("res://path/name.scn", scene) // or user://... + ResourceSaver.save("res://path/name.scn", scene) # or user://... [/codeblock] </description> <tutorials> diff --git a/doc/classes/PhysicsServer.xml b/doc/classes/PhysicsServer.xml index 88a104cb11..f79baea0be 100644 --- a/doc/classes/PhysicsServer.xml +++ b/doc/classes/PhysicsServer.xml @@ -1518,9 +1518,7 @@ <constant name="BODY_MODE_RIGID" value="2" enum="BodyMode"> Constant for rigid bodies. </constant> - <constant name="BODY_MODE_SOFT" value="3" enum="BodyMode"> - </constant> - <constant name="BODY_MODE_CHARACTER" value="4" enum="BodyMode"> + <constant name="BODY_MODE_CHARACTER" value="3" enum="BodyMode"> Constant for rigid bodies in character mode. In this mode, a body can not rotate, and only its linear velocity is affected by physics. </constant> <constant name="BODY_PARAM_BOUNCE" value="0" enum="BodyParameter"> diff --git a/doc/classes/PoolByteArray.xml b/doc/classes/PoolByteArray.xml index 765e68a623..ae722b1053 100644 --- a/doc/classes/PoolByteArray.xml +++ b/doc/classes/PoolByteArray.xml @@ -129,7 +129,7 @@ <argument index="1" name="to" type="int"> </argument> <description> - Returns the slice of the [code]PoolByteArray[/code] between indices (inclusive) as a new [code]PoolByteArray[/code]. Any negative index is considered to be from the end of the array. + Returns the slice of the [code]PoolByteArray[/code] between indices (inclusive) as a new [code]PoolByteArray[/code]. Any negative index is considered to be from the end of the array. </description> </method> </methods> diff --git a/doc/classes/RID.xml b/doc/classes/RID.xml index 9eb1462542..a289b68c9a 100644 --- a/doc/classes/RID.xml +++ b/doc/classes/RID.xml @@ -4,7 +4,7 @@ Handle for a [Resource]'s unique ID. </brief_description> <description> - The RID type is used to access the unique integer ID of a resource. They are opaque, so they do not grant access to the associated resource by themselves. They are used by and with the low-level Server classes such as [VisualServer]. + The RID type is used to access the unique integer ID of a resource. They are opaque, so they do not grant access to the associated resource by themselves. They are used by and with the low-level Server classes such as [VisualServer]. </description> <tutorials> </tutorials> diff --git a/doc/classes/RayCast2D.xml b/doc/classes/RayCast2D.xml index e4d1ecb7f8..afb80f2f6e 100644 --- a/doc/classes/RayCast2D.xml +++ b/doc/classes/RayCast2D.xml @@ -44,7 +44,7 @@ <return type="void"> </return> <description> - Updates the collision information for the ray. Use this method to update the collision information immediately instead of waiting for the next [code]_physics_process[/code] call, for example if the ray or its parent has changed state. Note: [code]enabled == true[/code] is not required for this to work. + Updates the collision information for the ray. Use this method to update the collision information immediately instead of waiting for the next [code]_physics_process[/code] call, for example if the ray or its parent has changed state. Note: [code]enabled == true[/code] is not required for this to work. </description> </method> <method name="get_collider" qualifiers="const"> diff --git a/doc/classes/RigidBody.xml b/doc/classes/RigidBody.xml index eea1e0321b..f9e0f821a9 100644 --- a/doc/classes/RigidBody.xml +++ b/doc/classes/RigidBody.xml @@ -88,7 +88,7 @@ <return type="Array"> </return> <description> - Return a list of the bodies colliding with this one. By default, number of max contacts reported is at 0 , see [method set_max_contacts_reported] to increase it. Note that the result of this test is not immediate after moving objects. For performance, list of collisions is updated once per frame and before the physics step. Consider using signals instead. + Return a list of the bodies colliding with this one. By default, number of max contacts reported is at 0 , see [method set_max_contacts_reported] to increase it. Note that the result of this test is not immediate after moving objects. For performance, list of collisions is updated once per frame and before the physics step. Consider using signals instead. </description> </method> <method name="set_axis_velocity"> diff --git a/doc/classes/ScrollContainer.xml b/doc/classes/ScrollContainer.xml index 02f58a88cb..d310561233 100644 --- a/doc/classes/ScrollContainer.xml +++ b/doc/classes/ScrollContainer.xml @@ -4,7 +4,7 @@ A helper node for displaying scrollable elements (e.g. lists). </brief_description> <description> - A ScrollContainer node with a [Control] child and scrollbar child ([HScrollbar], [VScrollBar], or both) will only draw the Control within the ScrollContainer area. Scrollbars will automatically be drawn at the right (for vertical) or bottom (for horizontal) and will enable dragging to move the viewable Control (and its children) within the ScrollContainer. Scrollbars will also automatically resize the grabber based on the minimum_size of the Control relative to the ScrollContainer. Works great with a [Panel] control. You can set EXPAND on children size flags, so they will upscale to ScrollContainer size if ScrollContainer size is bigger (scroll is invisible for chosen dimension). + A ScrollContainer node with a [Control] child and scrollbar child ([HScrollbar], [VScrollBar], or both) will only draw the Control within the ScrollContainer area. Scrollbars will automatically be drawn at the right (for vertical) or bottom (for horizontal) and will enable dragging to move the viewable Control (and its children) within the ScrollContainer. Scrollbars will also automatically resize the grabber based on the minimum_size of the Control relative to the ScrollContainer. Works great with a [Panel] control. You can set EXPAND on children size flags, so they will upscale to ScrollContainer size if ScrollContainer size is bigger (scroll is invisible for chosen dimension). </description> <tutorials> </tutorials> diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index 49549a9892..6f8c76d1ea 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -248,7 +248,7 @@ Amount to offset alternating tiles. Uses HALF_OFFSET_* constants. Default value: HALF_OFFSET_DISABLED. </member> <member name="cell_quadrant_size" type="int" setter="set_quadrant_size" getter="get_quadrant_size"> - The TileMap's quadrant size. Optimizes drawing by batching, using chunks of this size. Default value: 16. + The TileMap's quadrant size. Optimizes drawing by batching, using chunks of this size. Default value: 16. </member> <member name="cell_size" type="Vector2" setter="set_cell_size" getter="get_cell_size"> The TileMap's cell size. @@ -278,7 +278,7 @@ The TileMap orientation mode. Uses MODE_* constants. Default value: MODE_SQUARE. </member> <member name="occluder_light_mask" type="int" setter="set_occluder_light_mask" getter="get_occluder_light_mask"> - The light mask assigned to all light occluders in the TileMap. The TileSet's light occluders will cast shadows only from Light2D(s) that have the same light mask(s). + The light mask assigned to all light occluders in the TileMap. The TileSet's light occluders will cast shadows only from Light2D(s) that have the same light mask(s). </member> <member name="tile_set" type="TileSet" setter="set_tileset" getter="get_tileset"> The assigned [TileSet]. diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml index b98161e483..3f0d6317a6 100644 --- a/doc/classes/TileSet.xml +++ b/doc/classes/TileSet.xml @@ -85,7 +85,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Creates a new tile which will be referenced by the given ID. + Creates a new tile with the given ID. </description> </method> <method name="find_tile_by_name" qualifiers="const"> @@ -117,7 +117,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Removes the tile referenced by the given ID. + Removes the given tile ID. </description> </method> <method name="tile_add_shape"> @@ -134,6 +134,7 @@ <argument index="4" name="autotile_coord" type="Vector2" default="Vector2( 0, 0 )"> </argument> <description> + Adds a shape to the tile. </description> </method> <method name="tile_get_light_occluder" qualifiers="const"> @@ -142,7 +143,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Returns the light occluder of the tile. + Returns the tile's light occluder. </description> </method> <method name="tile_get_material" qualifiers="const"> @@ -151,7 +152,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Returns the material of the tile. + Returns the tile's material. </description> </method> <method name="tile_get_modulate" qualifiers="const"> @@ -160,6 +161,7 @@ <argument index="0" name="id" type="int"> </argument> <description> + Returns the tile's modulation color. </description> </method> <method name="tile_get_name" qualifiers="const"> @@ -168,7 +170,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Returns the name of the tile. + Returns the tile's name. </description> </method> <method name="tile_get_navigation_polygon" qualifiers="const"> @@ -195,6 +197,7 @@ <argument index="0" name="id" type="int"> </argument> <description> + Returns the tile's normal map texture. </description> </method> <method name="tile_get_occluder_offset" qualifiers="const"> @@ -223,6 +226,7 @@ <argument index="1" name="shape_id" type="int"> </argument> <description> + Returns a tile's given shape. </description> </method> <method name="tile_get_shape_count" qualifiers="const"> @@ -231,6 +235,7 @@ <argument index="0" name="id" type="int"> </argument> <description> + Returns the number of shapes assigned to a tile. </description> </method> <method name="tile_get_shape_offset" qualifiers="const"> @@ -241,6 +246,7 @@ <argument index="1" name="shape_id" type="int"> </argument> <description> + Returns the offset of a tile's shape. </description> </method> <method name="tile_get_shape_one_way" qualifiers="const"> @@ -251,6 +257,7 @@ <argument index="1" name="shape_id" type="int"> </argument> <description> + Returns the one-way collision value of a tile's shape. </description> </method> <method name="tile_get_shape_transform" qualifiers="const"> @@ -261,6 +268,7 @@ <argument index="1" name="shape_id" type="int"> </argument> <description> + Returns the [Transform2D] of a tile's sahpe. </description> </method> <method name="tile_get_shapes" qualifiers="const"> @@ -269,7 +277,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Returns the array of shapes of the tile. + Returns an array of the tile's shapes. </description> </method> <method name="tile_get_texture" qualifiers="const"> @@ -278,7 +286,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Returns the texture of the tile. + Returns the tile's texture. </description> </method> <method name="tile_get_texture_offset" qualifiers="const"> @@ -296,6 +304,7 @@ <argument index="0" name="id" type="int"> </argument> <description> + Returns the tile's [enum TileMode]. </description> </method> <method name="tile_get_z_index" qualifiers="const"> @@ -304,6 +313,7 @@ <argument index="0" name="id" type="int"> </argument> <description> + Returns the tile's z-index (drawing layer). </description> </method> <method name="tile_set_light_occluder"> @@ -391,7 +401,7 @@ <argument index="1" name="occluder_offset" type="Vector2"> </argument> <description> - Set an offset for the tile's light occluder. + Sets an offset for the tile's light occluder. </description> </method> <method name="tile_set_region"> @@ -402,7 +412,7 @@ <argument index="1" name="region" type="Rect2"> </argument> <description> - Set the tile's sub-region in the texture. This is common in texture atlases. + Sets the tile's sub-region in the texture. This is common in texture atlases. </description> </method> <method name="tile_set_shape"> @@ -415,6 +425,7 @@ <argument index="2" name="shape" type="Shape2D"> </argument> <description> + Sets a shape for the tile, enabling collision. </description> </method> <method name="tile_set_shape_offset"> @@ -427,6 +438,7 @@ <argument index="2" name="shape_offset" type="Vector2"> </argument> <description> + Sets the offset of a tile's shape. </description> </method> <method name="tile_set_shape_one_way"> @@ -439,6 +451,7 @@ <argument index="2" name="one_way" type="bool"> </argument> <description> + Enables one-way collision on a tile's shape. </description> </method> <method name="tile_set_shape_transform"> @@ -451,6 +464,7 @@ <argument index="2" name="shape_transform" type="Transform2D"> </argument> <description> + Sets a [Transform2D] on a tile's shape. </description> </method> <method name="tile_set_shapes"> diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml index ae8bdace73..0431718066 100644 --- a/doc/classes/Tree.xml +++ b/doc/classes/Tree.xml @@ -94,7 +94,7 @@ <argument index="0" name="position" type="Vector2"> </argument> <description> - If [member drop_mode_flags] includes [code]DROP_MODE_INBETWEEN[/code], returns -1 if [code]position[/code] is the upper part of a tree item at that position, 1 for the lower part, and additionally 0 for the middle part if [member drop_mode_flags] includes [code]DROP_MODE_ON_ITEM[/code]. + If [member drop_mode_flags] includes [code]DROP_MODE_INBETWEEN[/code], returns -1 if [code]position[/code] is the upper part of a tree item at that position, 1 for the lower part, and additionally 0 for the middle part if [member drop_mode_flags] includes [code]DROP_MODE_ON_ITEM[/code]. Otherwise, returns 0. If there are no tree item at [code]position[/code], returns -100. </description> </method> diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml index 123226183a..1dc03ed314 100644 --- a/doc/classes/Tween.xml +++ b/doc/classes/Tween.xml @@ -9,8 +9,8 @@ [codeblock] var tween = get_node("Tween") tween.interpolate_property($Node2D, "position", - Vector2(0, 0), Vector2(100, 100), 1, - Tween.TRANS_LINEAR, Tween.EASE_IN_OUT) + Vector2(0, 0), Vector2(100, 100), 1, + Tween.TRANS_LINEAR, Tween.EASE_IN_OUT) tween.start() [/codeblock] Many methods require a property name, such as "position" above. You can find the correct property name by hovering over the property in the Inspector. @@ -44,7 +44,7 @@ </argument> <description> Follows [code]method[/code] of [code]object[/code] and applies the returned value on [code]target_method[/code] of [code]target[/code], beginning from [code]initial_val[/code] for [code]duration[/code] seconds, [code]delay[/code] later. Methods are called with consecutive values. - Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information + Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information </description> </method> <method name="follow_property"> @@ -70,7 +70,7 @@ </argument> <description> Follows [code]property[/code] of [code]object[/code] and applies it on [code]target_property[/code] of [code]target[/code], beginning from [code]initial_val[/code] for [code]duration[/code] seconds, [code]delay[/code] seconds later. - Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information + Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information </description> </method> <method name="get_runtime" qualifiers="const"> @@ -147,7 +147,7 @@ </argument> <description> Animates [code]method[/code] of [code]object[/code] from [code]initial_val[/code] to [code]final_val[/code] for [code]duration[/code] seconds, [code]delay[/code] seconds later. Methods are called with consecutive values. - Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information + Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information </description> </method> <method name="interpolate_property"> @@ -171,7 +171,7 @@ </argument> <description> Animates [code]property[/code] of [code]object[/code] from [code]initial_val[/code] to [code]final_val[/code] for [code]duration[/code] seconds, [code]delay[/code] seconds later. Setting the initial value to [code]null[/code] uses the current value of the property. - Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information + Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information </description> </method> <method name="is_active" qualifiers="const"> @@ -301,7 +301,7 @@ </argument> <description> Animates [code]method[/code] of [code]object[/code] from the value returned by [code]initial_method[/code] to [code]final_val[/code] for [code]duration[/code] seconds, [code]delay[/code] seconds later. Methods are animated by calling them with consecutive values. - Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information + Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information </description> </method> <method name="targeting_property"> @@ -327,7 +327,7 @@ </argument> <description> Animates [code]property[/code] of [code]object[/code] from the current value of the [code]initial_val[/code] property of [code]initial[/code] to [code]final_val[/code] for [code]duration[/code] seconds, [code]delay[/code] seconds later. - Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information + Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information </description> </method> <method name="tell" qualifiers="const"> diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml index a721ef50c5..7e03e7f0fe 100644 --- a/doc/classes/Vector2.xml +++ b/doc/classes/Vector2.xml @@ -227,7 +227,7 @@ <argument index="1" name="t" type="float"> </argument> <description> - Returns the result of SLERP between this vector and [code]b[/code], by amount [code]t[/code]. [code]t[/code] is in the range of [code]0.0 - 1.0[/code], representing the amount of interpolation. + Returns the result of SLERP between this vector and [code]b[/code], by amount [code]t[/code]. [code]t[/code] is in the range of [code]0.0 - 1.0[/code], representing the amount of interpolation. Both vectors need to be normalized. </description> </method> diff --git a/doc/classes/VisibilityEnabler.xml b/doc/classes/VisibilityEnabler.xml index 83ba9495e5..5f0a4ef0f4 100644 --- a/doc/classes/VisibilityEnabler.xml +++ b/doc/classes/VisibilityEnabler.xml @@ -14,8 +14,10 @@ </methods> <members> <member name="freeze_bodies" type="bool" setter="set_enabler" getter="is_enabler_enabled"> + If [code]true[/code] [RigidBody] nodes will be paused. </member> <member name="pause_animations" type="bool" setter="set_enabler" getter="is_enabler_enabled"> + If [code]true[/code] [AnimationPlayer] nodes will be paused. </member> </members> <constants> diff --git a/doc/classes/VisibilityEnabler2D.xml b/doc/classes/VisibilityEnabler2D.xml index 8dfbaec6e8..eab9bd1991 100644 --- a/doc/classes/VisibilityEnabler2D.xml +++ b/doc/classes/VisibilityEnabler2D.xml @@ -14,16 +14,22 @@ </methods> <members> <member name="freeze_bodies" type="bool" setter="set_enabler" getter="is_enabler_enabled"> + If [code]true[/code] [RigidBody2D] nodes will be paused. </member> <member name="pause_animated_sprites" type="bool" setter="set_enabler" getter="is_enabler_enabled"> + If [code]true[/code] [AnimatedSprite] nodes will be paused. </member> <member name="pause_animations" type="bool" setter="set_enabler" getter="is_enabler_enabled"> + If [code]true[/code] [AnimationPlayer] nodes will be paused. </member> <member name="pause_particles" type="bool" setter="set_enabler" getter="is_enabler_enabled"> + If [code]true[/code] [Particles2D] nodes will be paused. </member> <member name="physics_process_parent" type="bool" setter="set_enabler" getter="is_enabler_enabled"> + If [code]true[/code] the parent's [method Node._physics_process] will be stopped. </member> <member name="process_parent" type="bool" setter="set_enabler" getter="is_enabler_enabled"> + If [code]true[/code] the parent's [method Node._process] will be stopped. </member> </members> <constants> diff --git a/doc/classes/float.xml b/doc/classes/float.xml index ef3c3d72eb..0c5536b5fe 100644 --- a/doc/classes/float.xml +++ b/doc/classes/float.xml @@ -35,7 +35,7 @@ <argument index="0" name="from" type="String"> </argument> <description> - Cast a [String] value to a floating point value. This method accepts float value strings like [code] '1.23' [/code] and exponential notation strings for its parameter so calling [code] float('1e3') [/code] will return 1000.0 and calling [code] float('1e-3') [/code] will return -0.001. + Cast a [String] value to a floating point value. This method accepts float value strings like [code] '1.23' [/code] and exponential notation strings for its parameter so calling [code] float('1e3') [/code] will return 1000.0 and calling [code] float('1e-3') [/code] will return 0.001. </description> </method> </methods> diff --git a/drivers/SCsub b/drivers/SCsub index f9cfa3fb05..320d4dc4bb 100644 --- a/drivers/SCsub +++ b/drivers/SCsub @@ -4,9 +4,6 @@ Import('env') env.drivers_sources = [] -if 'builtin_zlib' in env and env['builtin_zlib']: - SConscript("zlib/SCsub") - # OS drivers SConscript('unix/SCsub') SConscript('windows/SCsub') diff --git a/drivers/alsa/SCsub b/drivers/alsa/SCsub index ee39fd2631..28b315ae66 100644 --- a/drivers/alsa/SCsub +++ b/drivers/alsa/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.drivers_sources, "*.cpp") - -Export('env') diff --git a/drivers/alsamidi/SCsub b/drivers/alsamidi/SCsub index 233593b0f9..4c24925192 100644 --- a/drivers/alsamidi/SCsub +++ b/drivers/alsamidi/SCsub @@ -4,5 +4,3 @@ Import('env') # Driver source files env.add_source_files(env.drivers_sources, "*.cpp") - -Export('env') diff --git a/drivers/convex_decomp/SCsub b/drivers/convex_decomp/SCsub index f017e55120..65ba5332b7 100644 --- a/drivers/convex_decomp/SCsub +++ b/drivers/convex_decomp/SCsub @@ -11,6 +11,7 @@ thirdparty_sources = [ "b2Triangle.cpp", ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env.add_source_files(env.drivers_sources, thirdparty_sources) -Export('env') +env_thirdparty = env.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.drivers_sources, thirdparty_sources) diff --git a/drivers/coreaudio/SCsub b/drivers/coreaudio/SCsub index 233593b0f9..4c24925192 100644 --- a/drivers/coreaudio/SCsub +++ b/drivers/coreaudio/SCsub @@ -4,5 +4,3 @@ Import('env') # Driver source files env.add_source_files(env.drivers_sources, "*.cpp") - -Export('env') diff --git a/drivers/coremidi/SCsub b/drivers/coremidi/SCsub index 233593b0f9..4c24925192 100644 --- a/drivers/coremidi/SCsub +++ b/drivers/coremidi/SCsub @@ -4,5 +4,3 @@ Import('env') # Driver source files env.add_source_files(env.drivers_sources, "*.cpp") - -Export('env') diff --git a/drivers/dummy/rasterizer_dummy.h b/drivers/dummy/rasterizer_dummy.h index 126f23feeb..9315026623 100644 --- a/drivers/dummy/rasterizer_dummy.h +++ b/drivers/dummy/rasterizer_dummy.h @@ -517,6 +517,7 @@ public: void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) {} void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) {} void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) {} + void reflection_probe_set_resolution(RID p_probe, int p_resolution) {} AABB reflection_probe_get_aabb(RID p_probe) const { return AABB(); } VS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const { return VisualServer::REFLECTION_PROBE_UPDATE_ONCE; } @@ -802,6 +803,8 @@ public: _create_func = _create_current; } + virtual bool is_low_end() const { return true; } + RasterizerDummy() {} ~RasterizerDummy() {} }; diff --git a/drivers/gl_context/SCsub b/drivers/gl_context/SCsub index 4d66a9f9f1..efb26a7908 100644 --- a/drivers/gl_context/SCsub +++ b/drivers/gl_context/SCsub @@ -10,13 +10,14 @@ if (env["platform"] in ["haiku", "osx", "windows", "x11"]): ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env.add_source_files(env.drivers_sources, thirdparty_sources) env.Append(CPPPATH=[thirdparty_dir]) env.Append(CPPFLAGS=['-DGLAD_ENABLED']) env.Append(CPPFLAGS=['-DGLES_OVER_GL']) + env_thirdparty = env.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.drivers_sources, thirdparty_sources) + # Godot source files env.add_source_files(env.drivers_sources, "*.cpp") - -Export('env') diff --git a/drivers/gles2/SCsub b/drivers/gles2/SCsub index 2471dd3739..9923e52c73 100644 --- a/drivers/gles2/SCsub +++ b/drivers/gles2/SCsub @@ -2,6 +2,6 @@ Import('env') -env.add_source_files(env.drivers_sources,"*.cpp") +env.add_source_files(env.drivers_sources, "*.cpp") SConscript("shaders/SCsub") diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp index 4ae4441462..263f210fa2 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.cpp +++ b/drivers/gles2/rasterizer_canvas_gles2.cpp @@ -811,8 +811,6 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons bool rebind_shader = true; - Size2 rt_size = Size2(storage->frame.current_rt->width, storage->frame.current_rt->height); - state.current_tex = RID(); state.current_tex_ptr = NULL; state.current_normal = RID(); diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp index bd03bd71f6..1dd594cc20 100644 --- a/drivers/gles2/rasterizer_gles2.cpp +++ b/drivers/gles2/rasterizer_gles2.cpp @@ -32,7 +32,7 @@ #include "core/os/os.h" #include "core/project_settings.h" -#include "gl_context/context_gl.h" +#include "drivers/gl_context/context_gl.h" #define _EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 #define _EXT_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 diff --git a/drivers/gles2/rasterizer_gles2.h b/drivers/gles2/rasterizer_gles2.h index c76d5f7f20..45a9db73f2 100644 --- a/drivers/gles2/rasterizer_gles2.h +++ b/drivers/gles2/rasterizer_gles2.h @@ -66,6 +66,8 @@ public: static void make_current(); static void register_config(); + virtual bool is_low_end() const { return true; } + RasterizerGLES2(); ~RasterizerGLES2(); }; diff --git a/drivers/gles2/rasterizer_scene_gles2.cpp b/drivers/gles2/rasterizer_scene_gles2.cpp index 56605a9fe5..22cc45a0f6 100644 --- a/drivers/gles2/rasterizer_scene_gles2.cpp +++ b/drivers/gles2/rasterizer_scene_gles2.cpp @@ -437,29 +437,182 @@ void RasterizerSceneGLES2::reflection_atlas_set_subdivision(RID p_ref_atlas, int //////////////////////////////////////////////////// RID RasterizerSceneGLES2::reflection_probe_instance_create(RID p_probe) { - return RID(); + + RasterizerStorageGLES2::ReflectionProbe *probe = storage->reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!probe, RID()); + + ReflectionProbeInstance *rpi = memnew(ReflectionProbeInstance); + + rpi->probe_ptr = probe; + rpi->self = reflection_probe_instance_owner.make_rid(rpi); + rpi->probe = p_probe; + rpi->reflection_atlas_index = -1; + rpi->render_step = -1; + rpi->last_pass = 0; + rpi->current_resolution = 0; + rpi->dirty = true; + + rpi->last_pass = 0; + rpi->index = 0; + + for (int i = 0; i < 6; i++) { + glGenFramebuffers(1, &rpi->fbo[i]); + } + + glGenFramebuffers(1, &rpi->fbo_blur); + glGenRenderbuffers(1, &rpi->depth); + glGenTextures(1, &rpi->cubemap); + + return rpi->self; } void RasterizerSceneGLES2::reflection_probe_instance_set_transform(RID p_instance, const Transform &p_transform) { + + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND(!rpi); + rpi->transform = p_transform; } void RasterizerSceneGLES2::reflection_probe_release_atlas_index(RID p_instance) { } bool RasterizerSceneGLES2::reflection_probe_instance_needs_redraw(RID p_instance) { - return false; + const ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!rpi, false); + + bool need_redraw = rpi->probe_ptr->resolution != rpi->current_resolution || rpi->dirty || rpi->probe_ptr->update_mode == VS::REFLECTION_PROBE_UPDATE_ALWAYS; + rpi->dirty = false; + return need_redraw; } bool RasterizerSceneGLES2::reflection_probe_instance_has_reflection(RID p_instance) { - return false; + return true; } bool RasterizerSceneGLES2::reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) { - return false; + + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!rpi, false); + + rpi->render_step = 0; + + if (rpi->probe_ptr->resolution != rpi->current_resolution) { + + //update cubemap if resolution changed + int size = rpi->probe_ptr->resolution; + rpi->current_resolution = size; + + int lod = 0; + + GLenum internal_format = GL_RGBA; + GLenum format = GL_RGBA; + GLenum type = GL_UNSIGNED_BYTE; + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, rpi->cubemap); + + // Set the initial (empty) mipmaps, all need to be set for this to work in GLES2, even if later wont be used. + while (size >= 1) { + + for (int i = 0; i < 6; i++) { + glTexImage2D(_cube_side_enum[i], lod, internal_format, size, size, 0, format, type, NULL); + if (size == rpi->current_resolution) { + //adjust framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, rpi->fbo[i]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _cube_side_enum[i], rpi->cubemap, 0); + glBindRenderbuffer(GL_RENDERBUFFER, rpi->depth); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size, size); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rpi->depth); + +#ifdef DEBUG_ENABLED + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + ERR_CONTINUE(status != GL_FRAMEBUFFER_COMPLETE); +#endif + } + } + + lod++; + + size >>= 1; + } + + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + + return true; } bool RasterizerSceneGLES2::reflection_probe_instance_postprocess_step(RID p_instance) { - return false; + + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_instance); + ERR_FAIL_COND_V(!rpi, false); + + int size = rpi->probe_ptr->resolution; + + { + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_BLEND); + glDepthMask(GL_FALSE); + + for (int i = 0; i < VS::ARRAY_MAX - 1; i++) { + glDisableVertexAttribArray(i); + } + } + + //vdc cache + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, storage->resources.radical_inverse_vdc_cache_tex); + + glBindFramebuffer(GL_FRAMEBUFFER, rpi->fbo_blur); + // now render to the framebuffer, mipmap level for mipmap level + int lod = 1; + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, rpi->cubemap); + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //use linear, no mipmaps so it does not read from what is being written to + + size >>= 1; + int mipmaps = 6; + int mm_level = mipmaps - 1; + + storage->shaders.cubemap_filter.set_conditional(CubemapFilterShaderGLES2::USE_SOURCE_PANORAMA, false); + storage->shaders.cubemap_filter.bind(); + + //blur + while (size >= 1) { + + for (int i = 0; i < 6; i++) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _cube_side_enum[i], rpi->cubemap, lod); + + glViewport(0, 0, size, size); + storage->bind_quad_array(); + storage->shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::FACE_ID, i); + float roughness = CLAMP(lod / (float)(mipmaps - 1), 0, 1); + storage->shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::ROUGHNESS, roughness); + storage->shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::Z_FLIP, false); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + + size >>= 1; + + mm_level--; + + lod++; + } + + // restore ranges + + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + return true; } /* ENVIRONMENT API */ @@ -712,10 +865,8 @@ void RasterizerSceneGLES2::_add_geometry_with_material(RasterizerStorageGLES2::G bool has_alpha = has_base_alpha || has_blend_alpha; bool mirror = p_instance->mirror; - bool no_cull = false; if (p_material->shader->spatial.cull_mode == RasterizerStorageGLES2::Shader::Spatial::CULL_MODE_DISABLED) { - no_cull = true; mirror = false; } else if (p_material->shader->spatial.cull_mode == RasterizerStorageGLES2::Shader::Spatial::CULL_MODE_FRONT) { mirror = !mirror; @@ -738,7 +889,6 @@ void RasterizerSceneGLES2::_add_geometry_with_material(RasterizerStorageGLES2::G //shader does not use discard and does not write a vertex position, use generic material if (p_instance->cast_shadows == VS::SHADOW_CASTING_SETTING_DOUBLE_SIDED) { p_material = storage->material_owner.getptr(!p_shadow_pass && p_material->shader->spatial.uses_world_coordinates ? default_worldcoord_material_twosided : default_material_twosided); - no_cull = true; mirror = false; } else { p_material = storage->material_owner.getptr(!p_shadow_pass && p_material->shader->spatial.uses_world_coordinates ? default_worldcoord_material : default_material); @@ -763,6 +913,7 @@ void RasterizerSceneGLES2::_add_geometry_with_material(RasterizerStorageGLES2::G e->use_accum = false; e->light_index = RenderList::MAX_LIGHTS; e->use_accum_ptr = &e->use_accum; + e->instancing = (e->instance->base_type == VS::INSTANCE_MULTIMESH) ? 1 : 0; if (e->geometry->last_pass != render_pass) { e->geometry->last_pass = render_pass; @@ -782,17 +933,39 @@ void RasterizerSceneGLES2::_add_geometry_with_material(RasterizerStorageGLES2::G e->material_index = e->material->index; - e->refprobe_0_index = 0xFF; //refprobe disabled by default - e->refprobe_1_index = 0xFF; //refprobe disabled by default + e->refprobe_0_index = RenderList::MAX_REFLECTION_PROBES; //refprobe disabled by default + e->refprobe_1_index = RenderList::MAX_REFLECTION_PROBES; //refprobe disabled by default if (!p_depth_pass) { e->depth_layer = e->instance->depth_layer; e->priority = p_material->render_priority; - //if (e->instance->reflection_probe_instances.size() > 0 ) { - // RasterizerStorageGLES2:: - //} + int rpsize = e->instance->reflection_probe_instances.size(); + if (rpsize > 0) { + bool first = true; + rpsize = MIN(rpsize, 2); //more than 2 per object are not supported, this keeps it stable + + for (int i = 0; i < rpsize; i++) { + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(e->instance->reflection_probe_instances[i]); + if (rpi->last_pass != render_pass) { + continue; + } + if (first) { + e->refprobe_0_index = rpi->index; + first = false; + } else { + e->refprobe_1_index = rpi->index; + break; + } + } + + /* if (e->refprobe_0_index > e->refprobe_1_index) { //if both are valid, swap them to keep order as best as possible + uint64_t tmp = e->refprobe_0_index; + e->refprobe_0_index = e->refprobe_1_index; + e->refprobe_1_index = tmp; + }*/ + } //add directional lights @@ -924,9 +1097,7 @@ void RasterizerSceneGLES2::_fill_render_list(InstanceBase **p_cull_result, int p } break; - default: { - - } break; + default: {} } } } @@ -1051,7 +1222,7 @@ void RasterizerSceneGLES2::_setup_geometry(RenderList::Element *p_element, Raste glDisableVertexAttribArray(i); switch (i) { case VS::ARRAY_NORMAL: { - glVertexAttrib4f(VS::ARRAY_COLOR, 0.0, 0.0, 1, 1); + glVertexAttrib4f(VS::ARRAY_NORMAL, 0.0, 0.0, 1, 1); } break; case VS::ARRAY_COLOR: { glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); @@ -1136,8 +1307,6 @@ void RasterizerSceneGLES2::_setup_geometry(RenderList::Element *p_element, Raste bone_weight[3] = (weight_ptr[3] / (float)0xFFFF); } - size_t offset = i * 12; - Transform transform; Transform bone_transforms[4] = { @@ -1176,33 +1345,28 @@ void RasterizerSceneGLES2::_setup_geometry(RenderList::Element *p_element, Raste //enable transform buffer and bind it glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer); - glEnableVertexAttribArray(VS::ARRAY_MAX + 0); - glEnableVertexAttribArray(VS::ARRAY_MAX + 1); - glEnableVertexAttribArray(VS::ARRAY_MAX + 2); + glEnableVertexAttribArray(INSTANCE_BONE_BASE + 0); + glEnableVertexAttribArray(INSTANCE_BONE_BASE + 1); + glEnableVertexAttribArray(INSTANCE_BONE_BASE + 2); - glVertexAttribPointer(VS::ARRAY_MAX + 0, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 0)); - glVertexAttribPointer(VS::ARRAY_MAX + 1, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 1)); - glVertexAttribPointer(VS::ARRAY_MAX + 2, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 2)); + glVertexAttribPointer(INSTANCE_BONE_BASE + 0, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 0)); + glVertexAttribPointer(INSTANCE_BONE_BASE + 1, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 1)); + glVertexAttribPointer(INSTANCE_BONE_BASE + 2, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 2)); clear_skeleton_buffer = false; } } if (clear_skeleton_buffer) { - // just to make sure - glDisableVertexAttribArray(VS::ARRAY_MAX + 0); - glDisableVertexAttribArray(VS::ARRAY_MAX + 1); - glDisableVertexAttribArray(VS::ARRAY_MAX + 2); - glVertexAttrib4f(VS::ARRAY_MAX + 0, 1, 0, 0, 0); - glVertexAttrib4f(VS::ARRAY_MAX + 1, 0, 1, 0, 0); - glVertexAttrib4f(VS::ARRAY_MAX + 2, 0, 0, 1, 0); + glDisableVertexAttribArray(INSTANCE_BONE_BASE + 0); + glDisableVertexAttribArray(INSTANCE_BONE_BASE + 1); + glDisableVertexAttribArray(INSTANCE_BONE_BASE + 2); } } break; case VS::INSTANCE_MULTIMESH: { - RasterizerStorageGLES2::MultiMesh *multi_mesh = static_cast<RasterizerStorageGLES2::MultiMesh *>(p_element->owner); RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry); glBindBuffer(GL_ARRAY_BUFFER, s->vertex_id); @@ -1219,7 +1383,7 @@ void RasterizerSceneGLES2::_setup_geometry(RenderList::Element *p_element, Raste glDisableVertexAttribArray(i); switch (i) { case VS::ARRAY_NORMAL: { - glVertexAttrib4f(VS::ARRAY_COLOR, 0.0, 0.0, 1, 1); + glVertexAttrib4f(VS::ARRAY_NORMAL, 0.0, 0.0, 1, 1); } break; case VS::ARRAY_COLOR: { glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); @@ -1230,25 +1394,22 @@ void RasterizerSceneGLES2::_setup_geometry(RenderList::Element *p_element, Raste } } - if (!storage->config.float_texture_supported) { - // just to make sure, clear skeleton buffer too - glDisableVertexAttribArray(VS::ARRAY_MAX + 0); - glDisableVertexAttribArray(VS::ARRAY_MAX + 1); - glDisableVertexAttribArray(VS::ARRAY_MAX + 2); - - glVertexAttrib4f(VS::ARRAY_MAX + 0, 1, 0, 0, 0); - glVertexAttrib4f(VS::ARRAY_MAX + 1, 0, 1, 0, 0); - glVertexAttrib4f(VS::ARRAY_MAX + 2, 0, 0, 1, 0); - } + // prepare multimesh (disable) + glDisableVertexAttribArray(INSTANCE_ATTRIB_BASE + 0); + glDisableVertexAttribArray(INSTANCE_ATTRIB_BASE + 1); + glDisableVertexAttribArray(INSTANCE_ATTRIB_BASE + 2); + glDisableVertexAttribArray(INSTANCE_ATTRIB_BASE + 3); + glDisableVertexAttribArray(INSTANCE_ATTRIB_BASE + 4); + glDisableVertexAttribArray(INSTANCE_BONE_BASE + 0); + glDisableVertexAttribArray(INSTANCE_BONE_BASE + 1); + glDisableVertexAttribArray(INSTANCE_BONE_BASE + 2); } break; case VS::INSTANCE_IMMEDIATE: { } break; - default: { - - } break; + default: {} } } @@ -1267,7 +1428,7 @@ void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) { } else { glDrawArrays(gl_primitive[s->primitive], 0, s->array_len); } - + /* if (p_element->instance->skeleton.is_valid() && s->attribs[VS::ARRAY_BONES].enabled && s->attribs[VS::ARRAY_WEIGHTS].enabled) { //clean up after skeleton glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer); @@ -1280,7 +1441,7 @@ void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) { glVertexAttrib4f(VS::ARRAY_MAX + 1, 0, 1, 0, 0); glVertexAttrib4f(VS::ARRAY_MAX + 2, 0, 0, 1, 0); } - +*/ } break; case VS::INSTANCE_MULTIMESH: { @@ -1301,53 +1462,33 @@ void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) { // drawing + const float *base_buffer = multi_mesh->data.ptr(); + for (int i = 0; i < amount; i++) { - float *buffer = &multi_mesh->data.write[i * stride]; + const float *buffer = base_buffer + i * stride; { - // inline of multimesh_get_transform since it's such a pain - // to get a RID from here... - Transform transform; - - transform.basis.elements[0][0] = buffer[0]; - transform.basis.elements[0][1] = buffer[1]; - transform.basis.elements[0][2] = buffer[2]; - transform.origin.x = buffer[3]; - transform.basis.elements[1][0] = buffer[4]; - transform.basis.elements[1][1] = buffer[5]; - transform.basis.elements[1][2] = buffer[6]; - transform.origin.y = buffer[7]; - transform.basis.elements[2][0] = buffer[8]; - transform.basis.elements[2][1] = buffer[9]; - transform.basis.elements[2][2] = buffer[10]; - transform.origin.z = buffer[11]; - - float row[3][4] = { - { transform.basis[0][0], transform.basis[0][1], transform.basis[0][2], transform.origin[0] }, - { transform.basis[1][0], transform.basis[1][1], transform.basis[1][2], transform.origin[1] }, - { transform.basis[2][0], transform.basis[2][1], transform.basis[2][2], transform.origin[2] }, - }; - - glVertexAttrib4fv(VS::ARRAY_MAX + 0, row[0]); - glVertexAttrib4fv(VS::ARRAY_MAX + 1, row[1]); - glVertexAttrib4fv(VS::ARRAY_MAX + 2, row[2]); + + glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 0, &buffer[0]); + glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 1, &buffer[4]); + glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 2, &buffer[8]); } if (multi_mesh->color_floats) { if (multi_mesh->color_format == VS::MULTIMESH_COLOR_8BIT) { uint8_t *color_data = (uint8_t *)(buffer + color_ofs); - glVertexAttrib4f(VS::ARRAY_MAX + 3, color_data[0] / 255.0, color_data[1] / 255.0, color_data[2] / 255.0, color_data[3] / 255.0); + glVertexAttrib4f(INSTANCE_ATTRIB_BASE + 3, color_data[0] / 255.0, color_data[1] / 255.0, color_data[2] / 255.0, color_data[3] / 255.0); } else { - glVertexAttrib4fv(VS::ARRAY_MAX + 3, buffer + color_ofs); + glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 3, buffer + color_ofs); } } if (multi_mesh->custom_data_floats) { if (multi_mesh->custom_data_format == VS::MULTIMESH_CUSTOM_DATA_8BIT) { uint8_t *custom_data = (uint8_t *)(buffer + custom_data_ofs); - glVertexAttrib4f(VS::ARRAY_MAX + 4, custom_data[0] / 255.0, custom_data[1] / 255.0, custom_data[2] / 255.0, custom_data[3] / 255.0); + glVertexAttrib4f(INSTANCE_ATTRIB_BASE + 4, custom_data[0] / 255.0, custom_data[1] / 255.0, custom_data[2] / 255.0, custom_data[3] / 255.0); } else { - glVertexAttrib4fv(VS::ARRAY_MAX + 4, buffer + custom_data_ofs); + glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 4, buffer + custom_data_ofs); } } @@ -1471,6 +1612,7 @@ void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) { } } break; + default: {} } } @@ -1514,7 +1656,7 @@ void RasterizerSceneGLES2::_setup_light_type(LightInstance *p_light, ShadowAtlas } state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, p_light->light_ptr->directional_blend_splits); - if (p_light->light_ptr->shadow) { + if (!state.render_no_shadows && p_light->light_ptr->shadow) { state.scene_shader.set_conditional(SceneShaderGLES2::USE_SHADOW, true); glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 3); glBindTexture(GL_TEXTURE_2D, directional_shadow.depth); @@ -1526,7 +1668,7 @@ void RasterizerSceneGLES2::_setup_light_type(LightInstance *p_light, ShadowAtlas case VS::LIGHT_OMNI: { state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_MODE_OMNI, true); - if (shadow_atlas && p_light->light_ptr->shadow) { + if (!state.render_no_shadows && shadow_atlas && p_light->light_ptr->shadow) { state.scene_shader.set_conditional(SceneShaderGLES2::USE_SHADOW, true); glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 3); glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth); @@ -1537,7 +1679,7 @@ void RasterizerSceneGLES2::_setup_light_type(LightInstance *p_light, ShadowAtlas case VS::LIGHT_SPOT: { state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_MODE_SPOT, true); - if (shadow_atlas && p_light->light_ptr->shadow) { + if (!state.render_no_shadows && shadow_atlas && p_light->light_ptr->shadow) { state.scene_shader.set_conditional(SceneShaderGLES2::USE_SHADOW, true); glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 3); glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth); @@ -1571,7 +1713,7 @@ void RasterizerSceneGLES2::_setup_light(LightInstance *light, ShadowAtlas *shado CameraMatrix matrices[4]; - if (light_ptr->shadow && directional_shadow.depth) { + if (!state.render_no_shadows && light_ptr->shadow && directional_shadow.depth) { int shadow_count = 0; Color split_offsets; @@ -1662,11 +1804,10 @@ void RasterizerSceneGLES2::_setup_light(LightInstance *light, ShadowAtlas *shado float range = light_ptr->param[VS::LIGHT_PARAM_RANGE]; state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_RANGE, range); - Color attenuation = Color(0.0, 0.0, 0.0, 0.0); - attenuation.a = light_ptr->param[VS::LIGHT_PARAM_ATTENUATION]; + float attenuation = light_ptr->param[VS::LIGHT_PARAM_ATTENUATION]; state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_ATTENUATION, attenuation); - if (light_ptr->shadow && shadow_atlas->shadow_owners.has(light->self)) { + if (!state.render_no_shadows && light_ptr->shadow && shadow_atlas && shadow_atlas->shadow_owners.has(light->self)) { uint32_t key = shadow_atlas->shadow_owners[light->self]; @@ -1716,8 +1857,7 @@ void RasterizerSceneGLES2::_setup_light(LightInstance *light, ShadowAtlas *shado Vector3 direction = p_view_transform.inverse().basis.xform(light->transform.basis.xform(Vector3(0, 0, -1))).normalized(); state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_DIRECTION, direction); - Color attenuation = Color(0.0, 0.0, 0.0, 0.0); - attenuation.a = light_ptr->param[VS::LIGHT_PARAM_ATTENUATION]; + float attenuation = light_ptr->param[VS::LIGHT_PARAM_ATTENUATION]; float range = light_ptr->param[VS::LIGHT_PARAM_RANGE]; float spot_attenuation = light_ptr->param[VS::LIGHT_PARAM_SPOT_ATTENUATION]; float angle = light_ptr->param[VS::LIGHT_PARAM_SPOT_ANGLE]; @@ -1728,7 +1868,7 @@ void RasterizerSceneGLES2::_setup_light(LightInstance *light, ShadowAtlas *shado state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPOT_ANGLE, angle); state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_RANGE, range); - if (light->light_ptr->shadow && shadow_atlas && shadow_atlas->shadow_owners.has(light->self)) { + if (!state.render_no_shadows && light->light_ptr->shadow && shadow_atlas && shadow_atlas->shadow_owners.has(light->self)) { uint32_t key = shadow_atlas->shadow_owners[light->self]; uint32_t quadrant = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x03; @@ -1773,8 +1913,56 @@ void RasterizerSceneGLES2::_setup_light(LightInstance *light, ShadowAtlas *shado } } break; + default: {} + } +} + +void RasterizerSceneGLES2::_setup_refprobes(ReflectionProbeInstance *p_refprobe1, ReflectionProbeInstance *p_refprobe2, const Transform &p_view_transform, Environment *p_env) { + + if (p_refprobe1) { + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE1_USE_BOX_PROJECT, p_refprobe1->probe_ptr->box_projection); + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE1_BOX_EXTENTS, p_refprobe1->probe_ptr->extents); + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE1_BOX_OFFSET, p_refprobe1->probe_ptr->origin_offset); + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE1_EXTERIOR, !p_refprobe1->probe_ptr->interior); + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE1_INTENSITY, p_refprobe1->probe_ptr->intensity); + + Color ambient; + if (p_refprobe1->probe_ptr->interior) { + ambient = p_refprobe1->probe_ptr->interior_ambient * p_refprobe1->probe_ptr->interior_ambient_energy; + ambient.a = p_refprobe1->probe_ptr->interior_ambient_probe_contrib; + } else if (p_env) { + ambient = p_env->ambient_color * p_env->ambient_energy; + ambient.a = p_env->ambient_sky_contribution; + } + + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE1_AMBIENT, ambient); + + Transform proj = (p_view_transform.inverse() * p_refprobe1->transform).affine_inverse(); + + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE1_LOCAL_MATRIX, proj); + } + + if (p_refprobe2) { + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_USE_BOX_PROJECT, p_refprobe2->probe_ptr->box_projection); + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_BOX_EXTENTS, p_refprobe2->probe_ptr->extents); + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_BOX_OFFSET, p_refprobe2->probe_ptr->origin_offset); + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_EXTERIOR, !p_refprobe2->probe_ptr->interior); + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_INTENSITY, p_refprobe2->probe_ptr->intensity); + + Color ambient; + if (p_refprobe2->probe_ptr->interior) { + ambient = p_refprobe2->probe_ptr->interior_ambient * p_refprobe2->probe_ptr->interior_ambient_energy; + ambient.a = p_refprobe2->probe_ptr->interior_ambient_probe_contrib; + } else if (p_env) { + ambient = p_env->ambient_color * p_env->ambient_energy; + ambient.a = p_env->ambient_sky_contribution; + } - default: break; + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_AMBIENT, ambient); + + Transform proj = (p_view_transform.inverse() * p_refprobe2->transform).affine_inverse(); + + state.scene_shader.set_uniform(SceneShaderGLES2::REFPROBE2_LOCAL_MATRIX, proj); } } @@ -1782,9 +1970,7 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas); - Vector2 screen_pixel_size; - screen_pixel_size.x = 1.0 / storage->frame.current_rt->width; - screen_pixel_size.y = 1.0 / storage->frame.current_rt->height; + Vector2 screen_pixel_size = state.screen_pixel_size; bool use_radiance_map = false; if (!p_shadow && p_base_env) { @@ -1800,6 +1986,7 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, RasterizerStorageGLES2::Material *prev_material = NULL; RasterizerStorageGLES2::Geometry *prev_geometry = NULL; RasterizerStorageGLES2::Skeleton *prev_skeleton = NULL; + RasterizerStorageGLES2::GeometryOwner *prev_owner = NULL; Transform view_transform_inverse = p_view_transform.inverse(); CameraMatrix projection_inverse = p_projection.inverse(); @@ -1807,6 +1994,8 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, bool prev_base_pass = false; LightInstance *prev_light = NULL; bool prev_vertex_lit = false; + ReflectionProbeInstance *prev_refprobe_1 = NULL; + ReflectionProbeInstance *prev_refprobe_2 = NULL; int prev_blend_mode = -2; //will always catch the first go @@ -1816,6 +2005,10 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, glDisable(GL_BLEND); } + RasterizerStorageGLES2::Texture *prev_lightmap = NULL; + float lightmap_energy = 1.0; + bool prev_use_lightmap_capture = false; + for (int i = 0; i < p_element_count; i++) { RenderList::Element *e = p_elements[i]; @@ -1825,6 +2018,13 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, bool accum_pass = *e->use_accum_ptr; *e->use_accum_ptr = true; //set to accum for next time this is found LightInstance *light = NULL; + ReflectionProbeInstance *refprobe_1 = NULL; + ReflectionProbeInstance *refprobe_2 = NULL; + RasterizerStorageGLES2::Texture *lightmap = NULL; + bool use_lightmap_capture = false; + bool rebind_light = false; + bool rebind_reflection = false; + bool rebind_lightmap = false; if (!p_shadow) { @@ -1860,6 +2060,7 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, _setup_light_type(light, shadow_atlas); rebind = true; + rebind_light = true; } int blend_mode = p_alpha_pass ? material->shader->spatial.blend_mode : -1; // -1 no blend, no mix @@ -1921,9 +2122,63 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, state.scene_shader.set_conditional(SceneShaderGLES2::USE_VERTEX_LIGHTING, vertex_lit); prev_vertex_lit = vertex_lit; } + + if (!unshaded && !accum_pass && e->refprobe_0_index != RenderList::MAX_REFLECTION_PROBES) { + ERR_FAIL_INDEX(e->refprobe_0_index, reflection_probe_count); + refprobe_1 = reflection_probe_instances[e->refprobe_0_index]; + } + if (!unshaded && !accum_pass && e->refprobe_1_index != RenderList::MAX_REFLECTION_PROBES) { + ERR_FAIL_INDEX(e->refprobe_1_index, reflection_probe_count); + refprobe_2 = reflection_probe_instances[e->refprobe_1_index]; + } + + if (refprobe_1 != prev_refprobe_1 || refprobe_2 != prev_refprobe_2) { + state.scene_shader.set_conditional(SceneShaderGLES2::USE_REFLECTION_PROBE1, refprobe_1 != NULL); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_REFLECTION_PROBE2, refprobe_2 != NULL); + if (refprobe_1 != NULL && refprobe_1 != prev_refprobe_1) { + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 5); + glBindTexture(GL_TEXTURE_CUBE_MAP, refprobe_1->cubemap); + } + if (refprobe_2 != NULL && refprobe_2 != prev_refprobe_2) { + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 6); + glBindTexture(GL_TEXTURE_CUBE_MAP, refprobe_2->cubemap); + } + rebind = true; + rebind_reflection = true; + } + + use_lightmap_capture = !unshaded && !accum_pass && !e->instance->lightmap_capture_data.empty(); + + if (use_lightmap_capture != prev_use_lightmap_capture) { + + state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP_CAPTURE, use_lightmap_capture); + rebind = true; + } + + if (!unshaded && !accum_pass && e->instance->lightmap.is_valid()) { + + lightmap = storage->texture_owner.getornull(e->instance->lightmap); + lightmap_energy = 1.0; + if (lightmap) { + RasterizerStorageGLES2::LightmapCapture *capture = storage->lightmap_capture_data_owner.getornull(e->instance->lightmap_capture->base); + if (capture) { + lightmap_energy = capture->energy; + } + } + } + + if (lightmap != prev_lightmap) { + state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP, lightmap != NULL); + if (lightmap != NULL) { + glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 4); + glBindTexture(GL_TEXTURE_2D, lightmap->tex_id); + } + rebind = true; + rebind_lightmap = true; + } } - bool instancing = e->instancing; + bool instancing = e->instance->base_type == VS::INSTANCE_MULTIMESH; if (instancing != prev_instancing) { @@ -1936,7 +2191,7 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, if (skeleton != prev_skeleton) { if (skeleton) { - state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON, skeleton != NULL); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON, true); state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON_SOFTWARE, !storage->config.float_texture_supported); } else { state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON, false); @@ -1946,7 +2201,7 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, rebind = true; } - if (e->geometry != prev_geometry || skeleton != prev_skeleton) { + if (e->owner != prev_owner || e->geometry != prev_geometry || skeleton != prev_skeleton) { _setup_geometry(e, skeleton); } @@ -1955,7 +2210,7 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, shader_rebind = _setup_material(material, p_reverse_cull, p_alpha_pass, Size2i(skeleton ? skeleton->size * 3 : 0, 0)); } - if (i == 0 || shader_rebind) { //first time must rebindmakin + if (i == 0 || shader_rebind) { //first time must rebind if (p_shadow) { state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_BIAS, p_shadow_bias); @@ -1972,6 +2227,7 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, if (p_env) { state.scene_shader.set_uniform(SceneShaderGLES2::BG_ENERGY, p_env->bg_energy); state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_SKY_CONTRIBUTION, p_env->ambient_sky_contribution); + state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_COLOR, p_env->ambient_color); state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_ENERGY, p_env->ambient_energy); @@ -1982,9 +2238,10 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_ENERGY, 1.0); } - if (light) { - _setup_light(light, shadow_atlas, p_view_transform); - } + //rebind all these + rebind_light = true; + rebind_reflection = true; + rebind_lightmap = true; } state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_MATRIX, view_transform_inverse); @@ -1998,25 +2255,53 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, state.scene_shader.set_uniform(SceneShaderGLES2::NORMAL_MULT, 1.0); // TODO mirror? } + if (rebind_light && light) { + _setup_light(light, shadow_atlas, p_view_transform); + } + + if (rebind_reflection && (refprobe_1 || refprobe_2)) { + _setup_refprobes(refprobe_1, refprobe_2, p_view_transform, p_env); + } + + if (rebind_lightmap && lightmap) { + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHTMAP_ENERGY, lightmap_energy); + } + state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform); + if (use_lightmap_capture) { //this is per instance, must be set always if present + glUniform4fv(state.scene_shader.get_uniform_location(SceneShaderGLES2::LIGHTMAP_CAPTURES), 12, (const GLfloat *)e->instance->lightmap_capture_data.ptr()); + state.scene_shader.set_uniform(SceneShaderGLES2::LIGHTMAP_CAPTURE_SKY, false); + } + _render_geometry(e); prev_geometry = e->geometry; + prev_owner = e->owner; prev_material = material; prev_skeleton = skeleton; prev_instancing = instancing; prev_light = light; + prev_refprobe_1 = refprobe_1; + prev_refprobe_2 = refprobe_2; + prev_lightmap = lightmap; + prev_use_lightmap_capture = use_lightmap_capture; } _setup_light_type(NULL, NULL); //clear light stuff + state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON, false); state.scene_shader.set_conditional(SceneShaderGLES2::SHADELESS, false); state.scene_shader.set_conditional(SceneShaderGLES2::BASE_PASS, false); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_INSTANCING, false); state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, false); state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM4, false); state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM2, false); state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, false); state.scene_shader.set_conditional(SceneShaderGLES2::USE_VERTEX_LIGHTING, false); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_REFLECTION_PROBE1, false); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_REFLECTION_PROBE2, false); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP, false); + state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP_CAPTURE, false); } void RasterizerSceneGLES2::_draw_sky(RasterizerStorageGLES2::Sky *p_sky, const CameraMatrix &p_projection, const Transform &p_transform, bool p_vflip, float p_custom_fov, float p_energy) { @@ -2111,6 +2396,36 @@ void RasterizerSceneGLES2::_draw_sky(RasterizerStorageGLES2::Sky *p_sky, const C void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID p_environment, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) { + GLuint current_fb = 0; + Environment *env = NULL; + + int viewport_width, viewport_height; + + if (p_reflection_probe.is_valid()) { + ReflectionProbeInstance *probe = reflection_probe_instance_owner.getornull(p_reflection_probe); + ERR_FAIL_COND(!probe); + state.render_no_shadows = !probe->probe_ptr->enable_shadows; + + if (!probe->probe_ptr->interior) { //use env only if not interior + env = environment_owner.getornull(p_environment); + } + + current_fb = probe->fbo[p_reflection_probe_pass]; + state.screen_pixel_size.x = 1.0 / probe->probe_ptr->resolution; + state.screen_pixel_size.y = 1.0 / probe->probe_ptr->resolution; + + viewport_width = probe->probe_ptr->resolution; + viewport_height = probe->probe_ptr->resolution; + + } else { + state.render_no_shadows = false; + current_fb = storage->frame.current_rt->fbo; + env = environment_owner.getornull(p_environment); + state.screen_pixel_size.x = 1.0 / storage->frame.current_rt->width; + state.screen_pixel_size.y = 1.0 / storage->frame.current_rt->height; + viewport_width = storage->frame.current_rt->width; + viewport_height = storage->frame.current_rt->height; + } //push back the directional lights if (p_light_cull_count) { @@ -2143,10 +2458,22 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const render_light_instance_count = 0; } - glEnable(GL_BLEND); + if (p_reflection_probe_cull_count) { - GLuint current_fb = storage->frame.current_rt->fbo; - Environment *env = environment_owner.getornull(p_environment); + reflection_probe_instances = (ReflectionProbeInstance **)alloca(sizeof(ReflectionProbeInstance *) * p_reflection_probe_cull_count); + reflection_probe_count = p_reflection_probe_cull_count; + for (int i = 0; i < p_reflection_probe_cull_count; i++) { + ReflectionProbeInstance *rpi = reflection_probe_instance_owner.getornull(p_reflection_probe_cull_result[i]); + ERR_CONTINUE(!rpi); + rpi->last_pass = render_pass + 1; //will be incremented later + rpi->index = i; + reflection_probe_instances[i] = rpi; + } + + } else { + reflection_probe_instances = NULL; + reflection_probe_count = 0; + } // render list stuff @@ -2156,6 +2483,7 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const // other stuff glBindFramebuffer(GL_FRAMEBUFFER, current_fb); + glViewport(0, 0, viewport_width, viewport_height); glDepthFunc(GL_LEQUAL); glDepthMask(GL_TRUE); @@ -2257,6 +2585,8 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const void RasterizerSceneGLES2::render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count) { + state.render_no_shadows = false; + LightInstance *light_instance = light_instance_owner.getornull(p_light); ERR_FAIL_COND(!light_instance); @@ -2267,7 +2597,6 @@ void RasterizerSceneGLES2::render_shadow(RID p_light, RID p_shadow_atlas, int p_ uint32_t y; uint32_t width; uint32_t height; - uint32_t vp_height; float zfar = 0; bool flip_facing = false; @@ -2353,14 +2682,12 @@ void RasterizerSceneGLES2::render_shadow(RID p_light, RID p_shadow_atlas, int p_ normal_bias = light->param[VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS] * bias_mult; fbo = directional_shadow.fbo; - vp_height = directional_shadow.size; } else { ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas); ERR_FAIL_COND(!shadow_atlas); ERR_FAIL_COND(!shadow_atlas->shadow_owners.has(p_light)); fbo = shadow_atlas->fbo; - vp_height = shadow_atlas->size; uint32_t key = shadow_atlas->shadow_owners[p_light]; @@ -2534,6 +2861,44 @@ void RasterizerSceneGLES2::set_scene_pass(uint64_t p_pass) { } bool RasterizerSceneGLES2::free(RID p_rid) { + + if (light_instance_owner.owns(p_rid)) { + + LightInstance *light_instance = light_instance_owner.getptr(p_rid); + + //remove from shadow atlases.. + for (Set<RID>::Element *E = light_instance->shadow_atlases.front(); E; E = E->next()) { + ShadowAtlas *shadow_atlas = shadow_atlas_owner.get(E->get()); + ERR_CONTINUE(!shadow_atlas->shadow_owners.has(p_rid)); + uint32_t key = shadow_atlas->shadow_owners[p_rid]; + uint32_t q = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x3; + uint32_t s = key & ShadowAtlas::SHADOW_INDEX_MASK; + + shadow_atlas->quadrants[q].shadows.write[s].owner = RID(); + shadow_atlas->shadow_owners.erase(p_rid); + } + + light_instance_owner.free(p_rid); + memdelete(light_instance); + + } else if (shadow_atlas_owner.owns(p_rid)) { + + ShadowAtlas *shadow_atlas = shadow_atlas_owner.get(p_rid); + shadow_atlas_set_size(p_rid, 0); + shadow_atlas_owner.free(p_rid); + memdelete(shadow_atlas); + } else if (reflection_probe_instance_owner.owns(p_rid)) { + + ReflectionProbeInstance *reflection_instance = reflection_probe_instance_owner.get(p_rid); + + reflection_probe_release_atlas_index(p_rid); + reflection_probe_instance_owner.free(p_rid); + memdelete(reflection_instance); + + } else { + return false; + } + return true; } @@ -2669,10 +3034,6 @@ void RasterizerSceneGLES2::initialize() { } shadow_filter_mode = SHADOW_FILTER_NEAREST; - - RenderList::Element e; - e.sort_key = 0; - e.light_type1 = 1; } void RasterizerSceneGLES2::iteration() { diff --git a/drivers/gles2/rasterizer_scene_gles2.h b/drivers/gles2/rasterizer_scene_gles2.h index 27cbc35299..14b9116952 100644 --- a/drivers/gles2/rasterizer_scene_gles2.h +++ b/drivers/gles2/rasterizer_scene_gles2.h @@ -59,6 +59,11 @@ public: SHADOW_FILTER_PCF13, }; + enum { + INSTANCE_ATTRIB_BASE = 8, + INSTANCE_BONE_BASE = 13, + }; + ShadowFilterMode shadow_filter_mode; RID default_material; @@ -204,6 +209,9 @@ public: float dual_parbolloid_direction; float dual_parbolloid_zfar; + bool render_no_shadows; + + Vector2 screen_pixel_size; } state; /* SHADOW ATLAS API */ @@ -287,6 +295,38 @@ public: /* REFLECTION PROBE INSTANCE */ + struct ReflectionProbeInstance : public RID_Data { + + RasterizerStorageGLES2::ReflectionProbe *probe_ptr; + RID probe; + RID self; + RID atlas; + + int reflection_atlas_index; + + int render_step; + int reflection_index; + + GLuint fbo[6]; + GLuint cubemap; + GLuint depth; + + GLuint fbo_blur; + + int current_resolution; + mutable bool dirty; + + uint64_t last_pass; + uint32_t index; + + Transform transform; + }; + + mutable RID_Owner<ReflectionProbeInstance> reflection_probe_instance_owner; + + ReflectionProbeInstance **reflection_probe_instances; + int reflection_probe_count; + virtual RID reflection_probe_instance_create(RID p_probe); virtual void reflection_probe_instance_set_transform(RID p_instance, const Transform &p_transform); virtual void reflection_probe_release_atlas_index(RID p_instance); @@ -424,6 +464,7 @@ public: enum { MAX_LIGHTS = 255, + MAX_REFLECTION_PROBES = 255, DEFAULT_MAX_ELEMENTS = 65536 }; @@ -587,6 +628,7 @@ public: _FORCE_INLINE_ void _setup_geometry(RenderList::Element *p_element, RasterizerStorageGLES2::Skeleton *p_skeleton); _FORCE_INLINE_ void _setup_light_type(LightInstance *p_light, ShadowAtlas *shadow_atlas); _FORCE_INLINE_ void _setup_light(LightInstance *p_light, ShadowAtlas *shadow_atlas, const Transform &p_view_transform); + _FORCE_INLINE_ void _setup_refprobes(ReflectionProbeInstance *p_refprobe1, ReflectionProbeInstance *p_refprobe2, const Transform &p_view_transform, Environment *p_env); _FORCE_INLINE_ void _render_geometry(RenderList::Element *p_element); virtual void render_scene(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID p_environment, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass); diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp index adf07c2e4f..6314a69a90 100644 --- a/drivers/gles2/rasterizer_storage_gles2.cpp +++ b/drivers/gles2/rasterizer_storage_gles2.cpp @@ -54,6 +54,8 @@ GLuint RasterizerStorageGLES2::system_fbo = 0; #define _EXT_TEXTURE_CUBE_MAP_SEAMLESS 0x884F +#define _DEPTH_COMPONENT24_OES 0x81A6 + void RasterizerStorageGLES2::bind_quad_array() const { glBindBuffer(GL_ARRAY_BUFFER, resources.quadie); glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, 0); @@ -356,7 +358,6 @@ void RasterizerStorageGLES2::texture_allocate(RID p_texture, int p_width, int p_ GLenum type; bool compressed = false; - bool srgb = false; if (p_flags & VS::TEXTURE_FLAG_USED_FOR_STREAMING) { p_flags &= ~VS::TEXTURE_FLAG_MIPMAPS; // no mipies for video @@ -497,22 +498,6 @@ void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p glTexParameterf(texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } -//set swizle for older format compatibility -#ifdef GLES_OVER_GL - switch (texture->format) { - - case Image::FORMAT_L8: { - - } break; - case Image::FORMAT_LA8: { - - } break; - default: { - - } break; - } -#endif - int mipmaps = ((texture->flags & VS::TEXTURE_FLAG_MIPMAPS) && img->has_mipmaps()) ? img->get_mipmap_count() + 1 : 1; int w = img->get_width(); @@ -592,7 +577,7 @@ Ref<Image> RasterizerStorageGLES2::texture_get_data(RID p_texture, int p_layer) PoolVector<uint8_t> data; - int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1 ? -1 : 0); + int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1); data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers PoolVector<uint8_t>::Write wb = data.write(); @@ -965,6 +950,7 @@ void RasterizerStorageGLES2::sky_set_texture(RID p_sky, RID p_panorama, int p_ra shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::FACE_ID, i); float roughness = mm_level ? lod / (float)(mipmaps - 1) : 1; + roughness = MIN(1.0, roughness); //keep max at 1 shaders.cubemap_filter.set_uniform(CubemapFilterShaderGLES2::ROUGHNESS, roughness); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); @@ -1310,8 +1296,13 @@ void RasterizerStorageGLES2::shader_get_param_list(RID p_shader, List<PropertyIn pi.hint_string = "CubeMap"; } break; - default: { - + case ShaderLanguage::TYPE_SAMPLER2DARRAY: + case ShaderLanguage::TYPE_ISAMPLER2DARRAY: + case ShaderLanguage::TYPE_USAMPLER2DARRAY: + case ShaderLanguage::TYPE_SAMPLER3D: + case ShaderLanguage::TYPE_ISAMPLER3D: + case ShaderLanguage::TYPE_USAMPLER3D: { + // Not implemented in GLES2 } break; } @@ -2636,10 +2627,10 @@ void RasterizerStorageGLES2::update_dirty_multimeshes() { if (multimesh->mesh.is_valid()) { mesh_aabb = mesh_get_aabb(multimesh->mesh, RID()); - } else { - mesh_aabb.size += Vector3(0.001, 0.001, 0.001); } + mesh_aabb.size += Vector3(0.001, 0.001, 0.001); //in case mesh is empty in one of the sides + int stride = multimesh->color_floats + multimesh->xform_floats + multimesh->custom_data_floats; int count = multimesh->data.size(); float *data = multimesh->data.ptrw(); @@ -3111,6 +3102,7 @@ void RasterizerStorageGLES2::light_set_param(RID p_light, VS::LightParam p_param light->version++; light->instance_change_notify(); } break; + default: {} } light->param[p_param] = p_value; @@ -3302,69 +3294,194 @@ AABB RasterizerStorageGLES2::light_get_aabb(RID p_light) const { /* PROBE API */ RID RasterizerStorageGLES2::reflection_probe_create() { - return RID(); + + ReflectionProbe *reflection_probe = memnew(ReflectionProbe); + + reflection_probe->intensity = 1.0; + reflection_probe->interior_ambient = Color(); + reflection_probe->interior_ambient_energy = 1.0; + reflection_probe->max_distance = 0; + reflection_probe->extents = Vector3(1, 1, 1); + reflection_probe->origin_offset = Vector3(0, 0, 0); + reflection_probe->interior = false; + reflection_probe->box_projection = false; + reflection_probe->enable_shadows = false; + reflection_probe->cull_mask = (1 << 20) - 1; + reflection_probe->update_mode = VS::REFLECTION_PROBE_UPDATE_ONCE; + reflection_probe->resolution = 128; + + return reflection_probe_owner.make_rid(reflection_probe); } void RasterizerStorageGLES2::reflection_probe_set_update_mode(RID p_probe, VS::ReflectionProbeUpdateMode p_mode) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->update_mode = p_mode; + reflection_probe->instance_change_notify(); } void RasterizerStorageGLES2::reflection_probe_set_intensity(RID p_probe, float p_intensity) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->intensity = p_intensity; } void RasterizerStorageGLES2::reflection_probe_set_interior_ambient(RID p_probe, const Color &p_ambient) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->interior_ambient = p_ambient; } void RasterizerStorageGLES2::reflection_probe_set_interior_ambient_energy(RID p_probe, float p_energy) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->interior_ambient_energy = p_energy; } void RasterizerStorageGLES2::reflection_probe_set_interior_ambient_probe_contribution(RID p_probe, float p_contrib) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->interior_ambient_probe_contrib = p_contrib; } void RasterizerStorageGLES2::reflection_probe_set_max_distance(RID p_probe, float p_distance) { -} -void RasterizerStorageGLES2::reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) { + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->max_distance = p_distance; + reflection_probe->instance_change_notify(); } +void RasterizerStorageGLES2::reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) { + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->extents = p_extents; + reflection_probe->instance_change_notify(); +} void RasterizerStorageGLES2::reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->origin_offset = p_offset; + reflection_probe->instance_change_notify(); } void RasterizerStorageGLES2::reflection_probe_set_as_interior(RID p_probe, bool p_enable) { -} + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->interior = p_enable; +} void RasterizerStorageGLES2::reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->box_projection = p_enable; } void RasterizerStorageGLES2::reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) { -} + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->enable_shadows = p_enable; + reflection_probe->instance_change_notify(); +} void RasterizerStorageGLES2::reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->cull_mask = p_layers; + reflection_probe->instance_change_notify(); +} + +void RasterizerStorageGLES2::reflection_probe_set_resolution(RID p_probe, int p_resolution) { + + ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->resolution = p_resolution; } AABB RasterizerStorageGLES2::reflection_probe_get_aabb(RID p_probe) const { - return AABB(); + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, AABB()); + + AABB aabb; + aabb.position = -reflection_probe->extents; + aabb.size = reflection_probe->extents * 2.0; + + return aabb; } VS::ReflectionProbeUpdateMode RasterizerStorageGLES2::reflection_probe_get_update_mode(RID p_probe) const { - return VS::REFLECTION_PROBE_UPDATE_ALWAYS; + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, VS::REFLECTION_PROBE_UPDATE_ALWAYS); + + return reflection_probe->update_mode; } uint32_t RasterizerStorageGLES2::reflection_probe_get_cull_mask(RID p_probe) const { - return 0; + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, 0); + + return reflection_probe->cull_mask; } Vector3 RasterizerStorageGLES2::reflection_probe_get_extents(RID p_probe) const { - return Vector3(); + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, Vector3()); + + return reflection_probe->extents; } Vector3 RasterizerStorageGLES2::reflection_probe_get_origin_offset(RID p_probe) const { - return Vector3(); + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, Vector3()); + + return reflection_probe->origin_offset; } bool RasterizerStorageGLES2::reflection_probe_renders_shadows(RID p_probe) const { - return false; + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, false); + + return reflection_probe->enable_shadows; } float RasterizerStorageGLES2::reflection_probe_get_origin_max_distance(RID p_probe) const { - return 0; + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, 0); + + return reflection_probe->max_distance; +} + +int RasterizerStorageGLES2::reflection_probe_get_resolution(RID p_probe) const { + + const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); + ERR_FAIL_COND_V(!reflection_probe, 0); + + return reflection_probe->resolution; } RID RasterizerStorageGLES2::gi_probe_create() { @@ -3465,46 +3582,100 @@ void RasterizerStorageGLES2::gi_probe_dynamic_data_update(RID p_gi_probe_data, i /////// RID RasterizerStorageGLES2::lightmap_capture_create() { - return RID(); + + LightmapCapture *capture = memnew(LightmapCapture); + return lightmap_capture_data_owner.make_rid(capture); } void RasterizerStorageGLES2::lightmap_capture_set_bounds(RID p_capture, const AABB &p_bounds) { -} -AABB RasterizerStorageGLES2::lightmap_capture_get_bounds(RID p_capture) const { - return AABB(); + LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND(!capture); + capture->bounds = p_bounds; + capture->instance_change_notify(); } +AABB RasterizerStorageGLES2::lightmap_capture_get_bounds(RID p_capture) const { -void RasterizerStorageGLES2::lightmap_capture_set_octree(RID p_capture, const PoolVector<uint8_t> &p_octree) { + const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND_V(!capture, AABB()); + return capture->bounds; } +void RasterizerStorageGLES2::lightmap_capture_set_octree(RID p_capture, const PoolVector<uint8_t> &p_octree) { + + LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND(!capture); + + ERR_FAIL_COND(p_octree.size() == 0 || (p_octree.size() % sizeof(LightmapCaptureOctree)) != 0); + capture->octree.resize(p_octree.size() / sizeof(LightmapCaptureOctree)); + if (p_octree.size()) { + PoolVector<LightmapCaptureOctree>::Write w = capture->octree.write(); + PoolVector<uint8_t>::Read r = p_octree.read(); + copymem(w.ptr(), r.ptr(), p_octree.size()); + } + capture->instance_change_notify(); +} PoolVector<uint8_t> RasterizerStorageGLES2::lightmap_capture_get_octree(RID p_capture) const { - return PoolVector<uint8_t>(); + + const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND_V(!capture, PoolVector<uint8_t>()); + + if (capture->octree.size() == 0) + return PoolVector<uint8_t>(); + + PoolVector<uint8_t> ret; + ret.resize(capture->octree.size() * sizeof(LightmapCaptureOctree)); + { + PoolVector<LightmapCaptureOctree>::Read r = capture->octree.read(); + PoolVector<uint8_t>::Write w = ret.write(); + copymem(w.ptr(), r.ptr(), ret.size()); + } + + return ret; } void RasterizerStorageGLES2::lightmap_capture_set_octree_cell_transform(RID p_capture, const Transform &p_xform) { + LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND(!capture); + capture->cell_xform = p_xform; } Transform RasterizerStorageGLES2::lightmap_capture_get_octree_cell_transform(RID p_capture) const { - return Transform(); + const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND_V(!capture, Transform()); + return capture->cell_xform; } void RasterizerStorageGLES2::lightmap_capture_set_octree_cell_subdiv(RID p_capture, int p_subdiv) { + LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND(!capture); + capture->cell_subdiv = p_subdiv; } int RasterizerStorageGLES2::lightmap_capture_get_octree_cell_subdiv(RID p_capture) const { - return 0; + const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND_V(!capture, 0); + return capture->cell_subdiv; } void RasterizerStorageGLES2::lightmap_capture_set_energy(RID p_capture, float p_energy) { + + LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND(!capture); + capture->energy = p_energy; } float RasterizerStorageGLES2::lightmap_capture_get_energy(RID p_capture) const { - return 0.0; + + const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND_V(!capture, 0); + return capture->energy; } const PoolVector<RasterizerStorage::LightmapCaptureOctree> *RasterizerStorageGLES2::lightmap_capture_get_octree_ptr(RID p_capture) const { - return NULL; + const LightmapCapture *capture = lightmap_capture_data_owner.getornull(p_capture); + ERR_FAIL_COND_V(!capture, NULL); + return &capture->octree; } /////// @@ -3596,15 +3767,115 @@ void RasterizerStorageGLES2::update_particles() { //////// void RasterizerStorageGLES2::instance_add_skeleton(RID p_skeleton, RasterizerScene::InstanceBase *p_instance) { + + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND(!skeleton); + + skeleton->instances.insert(p_instance); } void RasterizerStorageGLES2::instance_remove_skeleton(RID p_skeleton, RasterizerScene::InstanceBase *p_instance) { + + Skeleton *skeleton = skeleton_owner.getornull(p_skeleton); + ERR_FAIL_COND(!skeleton); + + skeleton->instances.erase(p_instance); } void RasterizerStorageGLES2::instance_add_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance) { + + Instantiable *inst = NULL; + switch (p_instance->base_type) { + case VS::INSTANCE_MESH: { + inst = mesh_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + case VS::INSTANCE_MULTIMESH: { + inst = multimesh_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + case VS::INSTANCE_IMMEDIATE: { + inst = immediate_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + /*case VS::INSTANCE_PARTICLES: { + inst = particles_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break;*/ + case VS::INSTANCE_REFLECTION_PROBE: { + inst = reflection_probe_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + case VS::INSTANCE_LIGHT: { + inst = light_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + /*case VS::INSTANCE_GI_PROBE: { + inst = gi_probe_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break;*/ + case VS::INSTANCE_LIGHTMAP_CAPTURE: { + inst = lightmap_capture_data_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + default: { + if (!inst) { + ERR_FAIL(); + } + } + } + + inst->instance_list.add(&p_instance->dependency_item); } void RasterizerStorageGLES2::instance_remove_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance) { + + Instantiable *inst = NULL; + + switch (p_instance->base_type) { + case VS::INSTANCE_MESH: { + inst = mesh_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + case VS::INSTANCE_MULTIMESH: { + inst = multimesh_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + case VS::INSTANCE_IMMEDIATE: { + inst = immediate_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + /*case VS::INSTANCE_PARTICLES: { + inst = particles_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break;*/ + case VS::INSTANCE_REFLECTION_PROBE: { + inst = reflection_probe_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + case VS::INSTANCE_LIGHT: { + inst = light_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + /*case VS::INSTANCE_GI_PROBE: { + inst = gi_probe_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; */ + case VS::INSTANCE_LIGHTMAP_CAPTURE: { + inst = lightmap_capture_data_owner.getornull(p_base); + ERR_FAIL_COND(!inst); + } break; + default: { + + if (!inst) { + ERR_FAIL(); + } + } + } + + ERR_FAIL_COND(!inst); + + inst->instance_list.remove(&p_instance->dependency_item); } /* RENDER TARGET */ @@ -3646,7 +3917,7 @@ void RasterizerStorageGLES2::_render_target_allocate(RenderTarget *rt) { glGenRenderbuffers(1, &rt->depth); glBindRenderbuffer(GL_RENDERBUFFER, rt->depth); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, rt->width, rt->height); + glRenderbufferStorage(GL_RENDERBUFFER, _DEPTH_COMPONENT24_OES, rt->width, rt->height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rt->depth); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -3862,6 +4133,10 @@ VS::InstanceType RasterizerStorageGLES2::get_base_type(RID p_rid) const { return VS::INSTANCE_MULTIMESH; } else if (immediate_owner.owns(p_rid)) { return VS::INSTANCE_IMMEDIATE; + } else if (reflection_probe_owner.owns(p_rid)) { + return VS::INSTANCE_REFLECTION_PROBE; + } else if (lightmap_capture_data_owner.owns(p_rid)) { + return VS::INSTANCE_LIGHTMAP_CAPTURE; } else { return VS::INSTANCE_NONE; } @@ -4039,6 +4314,25 @@ bool RasterizerStorageGLES2::free(RID p_rid) { memdelete(light); return true; + } else if (reflection_probe_owner.owns(p_rid)) { + + // delete the texture + ReflectionProbe *reflection_probe = reflection_probe_owner.get(p_rid); + reflection_probe->instance_remove_deps(); + + reflection_probe_owner.free(p_rid); + memdelete(reflection_probe); + + return true; + } else if (lightmap_capture_data_owner.owns(p_rid)) { + + // delete the texture + LightmapCapture *lightmap_capture = lightmap_capture_data_owner.get(p_rid); + lightmap_capture->instance_remove_deps(); + + lightmap_capture_data_owner.free(p_rid); + memdelete(lightmap_capture); + return true; } else { return false; } @@ -4090,15 +4384,15 @@ void RasterizerStorageGLES2::initialize() { } config.shrink_textures_x2 = false; - config.float_texture_supported = config.extensions.find("GL_ARB_texture_float") != NULL || config.extensions.find("GL_OES_texture_float") != NULL; - config.s3tc_supported = config.extensions.find("GL_EXT_texture_compression_s3tc") != NULL; - config.etc1_supported = config.extensions.has("GL_OES_compressed_ETC1_RGB8_texture") != NULL; + + config.float_texture_supported = config.extensions.has("GL_ARB_texture_float") || config.extensions.has("GL_OES_texture_float"); + config.s3tc_supported = config.extensions.has("GL_EXT_texture_compression_s3tc"); + config.etc1_supported = config.extensions.has("GL_OES_compressed_ETC1_RGB8_texture"); frame.count = 0; frame.delta = 0; frame.current_rt = NULL; frame.clear_request = false; - // config.keep_original_textures = false; glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &config.max_texture_image_units); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &config.max_texture_size); @@ -4201,13 +4495,13 @@ void RasterizerStorageGLES2::initialize() { // radical inverse vdc cache texture // used for cubemap filtering - if (config.float_texture_supported) { + if (true /*||config.float_texture_supported*/) { //uint8 is similar and works everywhere glGenTextures(1, &resources.radical_inverse_vdc_cache_tex); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, resources.radical_inverse_vdc_cache_tex); - float radical_inverse[512]; + uint8_t radical_inverse[512]; for (uint32_t i = 0; i < 512; i++) { uint32_t bits = i; @@ -4219,11 +4513,10 @@ void RasterizerStorageGLES2::initialize() { bits = ((bits & 0x00FF00FF) << 8) | ((bits & 0xFF00FF00) >> 8); float value = float(bits) * 2.3283064365386963e-10; - - radical_inverse[i] = value; + radical_inverse[i] = uint8_t(CLAMP(value * 255.0, 0, 255)); } - glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 512, 1, 0, GL_LUMINANCE, GL_FLOAT, radical_inverse); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 512, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, radical_inverse); glBindTexture(GL_TEXTURE_2D, 0); } @@ -4251,6 +4544,7 @@ void RasterizerStorageGLES2::update_dirty_resources() { update_dirty_shaders(); update_dirty_materials(); update_dirty_skeletons(); + update_dirty_multimeshes(); } RasterizerStorageGLES2::RasterizerStorageGLES2() { diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h index e6708914ec..b42e2dfb1f 100644 --- a/drivers/gles2/rasterizer_storage_gles2.h +++ b/drivers/gles2/rasterizer_storage_gles2.h @@ -157,7 +157,7 @@ public: //////////////////////////////////DATA/////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////// - struct Instanciable : public RID_Data { + struct Instantiable : public RID_Data { SelfList<RasterizerScene::InstanceBase>::List instance_list; _FORCE_INLINE_ void instance_change_notify() { @@ -187,15 +187,15 @@ public: } } - Instanciable() {} + Instantiable() {} - virtual ~Instanciable() {} + virtual ~Instantiable() {} }; - struct GeometryOwner : public Instanciable { + struct GeometryOwner : public Instantiable { }; - struct Geometry : public Instanciable { + struct Geometry : public Instantiable { enum Type { GEOMETRY_INVALID, @@ -893,7 +893,7 @@ public: /* Light API */ - struct Light : Instanciable { + struct Light : Instantiable { VS::LightType type; float param[VS::LIGHT_PARAM_MAX]; @@ -955,6 +955,26 @@ public: virtual uint64_t light_get_version(RID p_light) const; /* PROBE API */ + + struct ReflectionProbe : Instantiable { + + VS::ReflectionProbeUpdateMode update_mode; + float intensity; + Color interior_ambient; + float interior_ambient_energy; + float interior_ambient_probe_contrib; + float max_distance; + Vector3 extents; + Vector3 origin_offset; + bool interior; + bool box_projection; + bool enable_shadows; + uint32_t cull_mask; + int resolution; + }; + + mutable RID_Owner<ReflectionProbe> reflection_probe_owner; + virtual RID reflection_probe_create(); virtual void reflection_probe_set_update_mode(RID p_probe, VS::ReflectionProbeUpdateMode p_mode); @@ -969,11 +989,14 @@ public: virtual void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable); virtual void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable); virtual void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers); + virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution); virtual AABB reflection_probe_get_aabb(RID p_probe) const; virtual VS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const; virtual uint32_t reflection_probe_get_cull_mask(RID p_probe) const; + virtual int reflection_probe_get_resolution(RID p_probe) const; + virtual Vector3 reflection_probe_get_extents(RID p_probe) const; virtual Vector3 reflection_probe_get_origin_offset(RID p_probe) const; virtual float reflection_probe_get_origin_max_distance(RID p_probe) const; @@ -1023,6 +1046,21 @@ public: /* LIGHTMAP */ + struct LightmapCapture : public Instantiable { + + PoolVector<LightmapCaptureOctree> octree; + AABB bounds; + Transform cell_xform; + int cell_subdiv; + float energy; + LightmapCapture() { + energy = 1.0; + cell_subdiv = 1; + } + }; + + mutable RID_Owner<LightmapCapture> lightmap_capture_data_owner; + virtual RID lightmap_capture_create(); virtual void lightmap_capture_set_bounds(RID p_capture, const AABB &p_bounds); virtual AABB lightmap_capture_get_bounds(RID p_capture) const; diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp index bedcfbb798..082c520480 100644 --- a/drivers/gles2/shader_compiler_gles2.cpp +++ b/drivers/gles2/shader_compiler_gles2.cpp @@ -643,11 +643,11 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener case SL::OP_MOD: { - code += "mod("; + code += "mod(float("; code += _dump_node_code(op_node->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += ", "; + code += "), float("; code += _dump_node_code(op_node->arguments[1], p_level, r_gen_code, p_actions, p_default_actions, p_assigning); - code += ")"; + code += "))"; } break; default: { diff --git a/drivers/gles2/shader_gles2.cpp b/drivers/gles2/shader_gles2.cpp index 650e8f7c42..628a57c06d 100644 --- a/drivers/gles2/shader_gles2.cpp +++ b/drivers/gles2/shader_gles2.cpp @@ -690,11 +690,6 @@ void ShaderGLES2::use_material(void *p_material) { Version *v = version_map.getptr(conditional_version); - CustomCode *cc = NULL; - if (v) { - cc = custom_code_map.getptr(v->code_version); - } - // bind uniforms for (Map<StringName, ShaderLanguage::ShaderNode::Uniform>::Element *E = material->shader->uniforms.front(); E; E = E->next()) { @@ -982,7 +977,7 @@ void ShaderGLES2::use_material(void *p_material) { value.second.resize(default_arg_size); - for (int i = 0; i < default_arg_size; i++) { + for (size_t i = 0; i < default_arg_size; i++) { if (is_float) { value.second.write[i].real = 0.0; } else { @@ -992,8 +987,6 @@ void ShaderGLES2::use_material(void *p_material) { } } - // GLint location = get_uniform_location(E->key()); - GLint location; if (v->custom_uniform_locations.has(E->key())) { location = v->custom_uniform_locations[E->key()]; @@ -1013,8 +1006,6 @@ void ShaderGLES2::use_material(void *p_material) { int tc = material->textures.size(); Pair<StringName, RID> *textures = material->textures.ptrw(); - ShaderLanguage::ShaderNode::Uniform::Hint *texture_hints = material->shader->texture_hints.ptrw(); - for (int i = 0; i < tc; i++) { Pair<ShaderLanguage::DataType, Vector<ShaderLanguage::ConstantNode::Value> > value; diff --git a/drivers/gles2/shader_gles2.h b/drivers/gles2/shader_gles2.h index 78e1962f80..9160a7c265 100644 --- a/drivers/gles2/shader_gles2.h +++ b/drivers/gles2/shader_gles2.h @@ -335,6 +335,19 @@ public: case ShaderLanguage::TYPE_SAMPLERCUBE: { } break; + + case ShaderLanguage::TYPE_SAMPLER2DARRAY: + case ShaderLanguage::TYPE_ISAMPLER2DARRAY: + case ShaderLanguage::TYPE_USAMPLER2DARRAY: + case ShaderLanguage::TYPE_SAMPLER3D: + case ShaderLanguage::TYPE_ISAMPLER3D: + case ShaderLanguage::TYPE_USAMPLER3D: { + // Not implemented in GLES2 + } break; + + case ShaderLanguage::TYPE_VOID: { + // Nothing to do? + } break; } } diff --git a/drivers/gles2/shaders/cubemap_filter.glsl b/drivers/gles2/shaders/cubemap_filter.glsl index 2a1ad8d8f2..b1553c7cd5 100644 --- a/drivers/gles2/shaders/cubemap_filter.glsl +++ b/drivers/gles2/shaders/cubemap_filter.glsl @@ -167,18 +167,21 @@ void main() { vec3 H = ImportanceSampleGGX(xi, roughness, N); vec3 V = N; - vec3 L = normalize(2.0 * dot(V, H) * H - V); + vec3 L = (2.0 * dot(V, H) * H - V); float NdotL = clamp(dot(N, L), 0.0, 1.0); if (NdotL > 0.0) { #ifdef USE_SOURCE_PANORAMA - sum.rgb += texturePanorama(source_panorama, L).rgb * NdotL; + vec3 val = texturePanorama(source_panorama, L).rgb; #else - L.y = -L.y; - sum.rgb += textureCubeLod(source_cube, L, 0.0).rgb * NdotL; + vec3 val = textureCubeLod(source_cube, L, 0.0).rgb; #endif + //mix using Linear, to approximate high end back-end + val = mix(pow((val + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), val * (1.0 / 12.92), vec3(lessThan(val, vec3(0.04045)))); + + sum.rgb += val * NdotL; sum.a += NdotL; } @@ -186,5 +189,8 @@ void main() { sum /= sum.a; + vec3 a = vec3(0.055); + sum.rgb = mix((vec3(1.0) + a) * pow(sum.rgb, vec3(1.0 / 2.4)) - a, 12.92 * sum.rgb, vec3(lessThan(sum.rgb, vec3(0.0031308)))); + gl_FragColor = vec4(sum.rgb, 1.0); } diff --git a/drivers/gles2/shaders/scene.glsl b/drivers/gles2/shaders/scene.glsl index da4c3a84f1..958de94485 100644 --- a/drivers/gles2/shaders/scene.glsl +++ b/drivers/gles2/shaders/scene.glsl @@ -15,6 +15,7 @@ precision highp int; #define M_PI 3.14159265359 + // // attributes // @@ -27,15 +28,15 @@ attribute vec3 normal_attrib; // attrib:1 attribute vec4 tangent_attrib; // attrib:2 #endif -#ifdef ENABLE_COLOR_INTERP +#if defined(ENABLE_COLOR_INTERP) attribute vec4 color_attrib; // attrib:3 #endif -#ifdef ENABLE_UV_INTERP +#if defined(ENABLE_UV_INTERP) attribute vec2 uv_attrib; // attrib:4 #endif -#ifdef ENABLE_UV2_INTERP +#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP) attribute vec2 uv2_attrib; // attrib:5 #endif @@ -43,9 +44,9 @@ attribute vec2 uv2_attrib; // attrib:5 #ifdef USE_SKELETON_SOFTWARE -attribute highp vec4 bone_transform_row_0; // attrib:8 -attribute highp vec4 bone_transform_row_1; // attrib:9 -attribute highp vec4 bone_transform_row_2; // attrib:10 +attribute highp vec4 bone_transform_row_0; // attrib:13 +attribute highp vec4 bone_transform_row_1; // attrib:14 +attribute highp vec4 bone_transform_row_2; // attrib:15 #else @@ -102,15 +103,15 @@ varying vec3 tangent_interp; varying vec3 binormal_interp; #endif -#ifdef ENABLE_COLOR_INTERP +#if defined(ENABLE_COLOR_INTERP) varying vec4 color_interp; #endif -#ifdef ENABLE_UV_INTERP +#if defined(ENABLE_UV_INTERP) varying vec2 uv_interp; #endif -#ifdef ENABLE_UV2_INTERP +#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP) varying vec2 uv2_interp; #endif @@ -130,11 +131,6 @@ uniform highp float shadow_dual_paraboloid_render_side; #if defined(USE_SHADOW) && defined(USE_LIGHTING) -#ifdef LIGHT_MODE_DIRECTIONAL -uniform highp sampler2D light_directional_shadow; // texunit:-3 -uniform highp vec4 light_split_offsets; -#endif - uniform highp mat4 light_shadow_matrix; varying highp vec4 shadow_coord; @@ -170,7 +166,7 @@ uniform vec3 light_direction; uniform vec3 light_position; uniform float light_range; -uniform vec4 light_attenuation; +uniform float light_attenuation; // spot uniform float light_spot_attenuation; @@ -262,6 +258,34 @@ void light_compute( #endif +#ifdef USE_VERTEX_LIGHTING + +#ifdef USE_REFLECTION_PROBE1 + +uniform mat4 refprobe1_local_matrix; +varying mediump vec4 refprobe1_reflection_normal_blend; +uniform vec3 refprobe1_box_extents; + +#ifndef USE_LIGHTMAP +varying mediump vec3 refprobe1_ambient_normal; +#endif + +#endif //reflection probe1 + +#ifdef USE_REFLECTION_PROBE2 + +uniform mat4 refprobe2_local_matrix; +varying mediump vec4 refprobe2_reflection_normal_blend; +uniform vec3 refprobe2_box_extents; + +#ifndef USE_LIGHTMAP +varying mediump vec3 refprobe2_ambient_normal; +#endif + +#endif //reflection probe2 + +#endif //vertex lighting for refprobes + void main() { highp vec4 vertex = vertex_attrib; @@ -277,6 +301,7 @@ void main() { vec4(0.0, 0.0, 0.0, 1.0)); world_matrix = world_matrix * transpose(m); } + #endif vec3 normal = normal_attrib * normal_mult; @@ -288,18 +313,18 @@ void main() { vec3 binormal = normalize(cross(normal, tangent) * binormalf); #endif -#ifdef ENABLE_COLOR_INTERP +#if defined(ENABLE_COLOR_INTERP) color_interp = color_attrib; #ifdef USE_INSTANCING color_interp *= instance_color; #endif #endif -#ifdef ENABLE_UV_INTERP +#if defined(ENABLE_UV_INTERP) uv_interp = uv_attrib; #endif -#ifdef ENABLE_UV2_INTERP +#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP) uv2_interp = uv2_attrib; #endif @@ -438,10 +463,15 @@ VERTEX_SHADER_CODE float normalized_distance = light_length / light_range; - float omni_attenuation = pow(1.0 - normalized_distance, light_attenuation.w); + if (normalized_distance < 1.0) { + + float omni_attenuation = pow(1.0 - normalized_distance, light_attenuation); - vec3 attenuation = vec3(omni_attenuation); - light_att = vec3(omni_attenuation); + vec3 attenuation = vec3(omni_attenuation); + light_att = vec3(omni_attenuation); + } else { + light_att = vec3(0.0); + } L = normalize(light_vec); @@ -453,17 +483,30 @@ VERTEX_SHADER_CODE float light_length = length(light_rel_vec); float normalized_distance = light_length / light_range; - float spot_attenuation = pow(1.0 - normalized_distance, light_attenuation.w); - vec3 spot_dir = light_direction; + if (normalized_distance < 1.0) { + + float spot_attenuation = pow(1.0 - normalized_distance, light_attenuation); + vec3 spot_dir = light_direction; - float spot_cutoff = light_spot_angle; + float spot_cutoff = light_spot_angle; - float scos = max(dot(-normalize(light_rel_vec), spot_dir), spot_cutoff); - float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_cutoff)); + float angle = dot(-normalize(light_rel_vec), spot_dir); - spot_attenuation *= 1.0 - pow(spot_rim, light_spot_attenuation); + if (angle > spot_cutoff) { + + float scos = max(angle, spot_cutoff); + float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_cutoff)); + + spot_attenuation *= 1.0 - pow(spot_rim, light_spot_attenuation); + + light_att = vec3(spot_attenuation); + } else { + light_att = vec3(0.0); + } + } else { + light_att = vec3(0.0); + } - light_att = vec3(spot_attenuation); L = normalize(light_rel_vec); #endif @@ -492,12 +535,55 @@ VERTEX_SHADER_CODE #if defined(LIGHT_USE_PSSM4) shadow_coord3 = light_shadow_matrix3 * vi4; - shadow_coord3 = light_shadow_matrix3 * vi4; + shadow_coord4 = light_shadow_matrix4 * vi4; #endif #endif //use shadow and use lighting +#ifdef USE_VERTEX_LIGHTING + +#ifdef USE_REFLECTION_PROBE1 + { + vec3 ref_normal = normalize(reflect(vertex_interp, normal_interp)); + vec3 local_pos = (refprobe1_local_matrix * vec4(vertex_interp, 1.0)).xyz; + vec3 inner_pos = abs(local_pos / refprobe1_box_extents); + float blend = max(inner_pos.x, max(inner_pos.y, inner_pos.z)); + + { + vec3 local_ref_vec = (refprobe1_local_matrix * vec4(ref_normal, 0.0)).xyz; + refprobe1_reflection_normal_blend.xyz = local_ref_vec; + refprobe1_reflection_normal_blend.a = blend; + } +#ifndef USE_LIGHTMAP + + refprobe1_ambient_normal = (refprobe1_local_matrix * vec4(normal_interp, 0.0)).xyz; +#endif + } + +#endif //USE_REFLECTION_PROBE1 + +#ifdef USE_REFLECTION_PROBE2 + { + vec3 ref_normal = normalize(reflect(vertex_interp, normal_interp)); + vec3 local_pos = (refprobe2_local_matrix * vec4(vertex_interp, 1.0)).xyz; + vec3 inner_pos = abs(local_pos / refprobe2_box_extents); + float blend = max(inner_pos.x, max(inner_pos.y, inner_pos.z)); + + { + vec3 local_ref_vec = (refprobe2_local_matrix * vec4(ref_normal, 0.0)).xyz; + refprobe2_reflection_normal_blend.xyz = local_ref_vec; + refprobe2_reflection_normal_blend.a = blend; + } +#ifndef USE_LIGHTMAP + + refprobe2_ambient_normal = (refprobe2_local_matrix * vec4(normal_interp, 0.0)).xyz; +#endif + } + +#endif //USE_REFLECTION_PROBE2 + +#endif //use vertex lighting gl_Position = projection_matrix * vec4(vertex_interp, 1.0); } @@ -537,7 +623,7 @@ uniform mat4 world_transform; uniform highp float time; -#ifdef SCREEN_UV_USED +#if defined(SCREEN_UV_USED) uniform vec2 screen_pixel_size; #endif @@ -548,10 +634,168 @@ uniform vec2 screen_pixel_size; uniform highp sampler2D screen_texture; //texunit:-4 #endif -#ifdef USE_RADIANCE_MAP +#ifdef USE_REFLECTION_PROBE1 + +#ifdef USE_VERTEX_LIGHTING + +varying mediump vec4 refprobe1_reflection_normal_blend; +#ifndef USE_LIGHTMAP +varying mediump vec3 refprobe1_ambient_normal; +#endif + +#else + +uniform bool refprobe1_use_box_project; +uniform vec3 refprobe1_box_extents; +uniform vec3 refprobe1_box_offset; +uniform mat4 refprobe1_local_matrix; + +#endif //use vertex lighting + +uniform bool refprobe1_exterior; + +uniform highp samplerCube reflection_probe1; //texunit:-5 + +uniform float refprobe1_intensity; +uniform vec4 refprobe1_ambient; + +#endif //USE_REFLECTION_PROBE1 + +#ifdef USE_REFLECTION_PROBE2 + +#ifdef USE_VERTEX_LIGHTING + +varying mediump vec4 refprobe2_reflection_normal_blend; +#ifndef USE_LIGHTMAP +varying mediump vec3 refprobe2_ambient_normal; +#endif + +#else + +uniform bool refprobe2_use_box_project; +uniform vec3 refprobe2_box_extents; +uniform vec3 refprobe2_box_offset; +uniform mat4 refprobe2_local_matrix; + +#endif //use vertex lighting + +uniform bool refprobe2_exterior; + +uniform highp samplerCube reflection_probe2; //texunit:-6 + +uniform float refprobe2_intensity; +uniform vec4 refprobe2_ambient; + +#endif //USE_REFLECTION_PROBE2 #define RADIANCE_MAX_LOD 6.0 +#if defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2) + +void reflection_process(samplerCube reflection_map, +#ifdef USE_VERTEX_LIGHTING + vec3 ref_normal, +#ifndef USE_LIGHTMAP + vec3 amb_normal, +#endif + float ref_blend, + +#else //no vertex lighting + vec3 normal, vec3 vertex, + mat4 local_matrix, + bool use_box_project, vec3 box_extents, vec3 box_offset, +#endif //vertex lighting + bool exterior, float intensity, vec4 ref_ambient, float roughness, vec3 ambient, vec3 skybox, inout highp vec4 reflection_accum, inout highp vec4 ambient_accum) { + + vec4 reflection; + +#ifdef USE_VERTEX_LIGHTING + + reflection.rgb = textureCubeLod(reflection_map, ref_normal, roughness * RADIANCE_MAX_LOD).rgb; + + float blend = ref_blend; //crappier blend formula for vertex + blend *= blend; + blend = max(0.0, 1.0 - blend); + +#else //fragment lighting + + vec3 local_pos = (local_matrix * vec4(vertex, 1.0)).xyz; + + if (any(greaterThan(abs(local_pos), box_extents))) { //out of the reflection box + return; + } + + vec3 inner_pos = abs(local_pos / box_extents); + float blend = max(inner_pos.x, max(inner_pos.y, inner_pos.z)); + blend = mix(length(inner_pos), blend, blend); + blend *= blend; + blend = max(0.0, 1.0 - blend); + + //reflect and make local + vec3 ref_normal = normalize(reflect(vertex, normal)); + ref_normal = (local_matrix * vec4(ref_normal, 0.0)).xyz; + + if (use_box_project) { //box project + + vec3 nrdir = normalize(ref_normal); + vec3 rbmax = (box_extents - local_pos) / nrdir; + vec3 rbmin = (-box_extents - local_pos) / nrdir; + + vec3 rbminmax = mix(rbmin, rbmax, vec3(greaterThan(nrdir, vec3(0.0, 0.0, 0.0)))); + + float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z); + vec3 posonbox = local_pos + nrdir * fa; + ref_normal = posonbox - box_offset.xyz; + } + + reflection.rgb = textureCubeLod(reflection_map, ref_normal, roughness * RADIANCE_MAX_LOD).rgb; +#endif + + if (exterior) { + reflection.rgb = mix(skybox, reflection.rgb, blend); + } + reflection.rgb *= intensity; + reflection.a = blend; + reflection.rgb *= blend; + + reflection_accum += reflection; + +#ifndef USE_LIGHTMAP + + vec4 ambient_out; +#ifndef USE_VERTEX_LIGHTING + + vec3 amb_normal = (local_matrix * vec4(normal, 0.0)).xyz; +#endif + + ambient_out.rgb = textureCubeLod(reflection_map, amb_normal, RADIANCE_MAX_LOD).rgb; + ambient_out.rgb = mix(ref_ambient.rgb, ambient_out.rgb, ref_ambient.a); + if (exterior) { + ambient_out.rgb = mix(ambient, ambient_out.rgb, blend); + } + + ambient_out.a = blend; + ambient_out.rgb *= blend; + ambient_accum += ambient_out; + +#endif +} + +#endif //use refprobe 1 or 2 + +#ifdef USE_LIGHTMAP +uniform mediump sampler2D lightmap; //texunit:-4 +uniform mediump float lightmap_energy; +#endif + +#ifdef USE_LIGHTMAP_CAPTURE +uniform mediump vec4[12] lightmap_captures; +uniform bool lightmap_capture_sky; + +#endif + +#ifdef USE_RADIANCE_MAP + uniform samplerCube radiance_map; // texunit:-2 uniform mat4 radiance_inverse_xform; @@ -583,7 +827,7 @@ uniform vec3 light_direction; // omni uniform vec3 light_position; -uniform vec4 light_attenuation; +uniform float light_attenuation; // spot uniform float light_spot_attenuation; @@ -640,15 +884,15 @@ varying vec3 tangent_interp; varying vec3 binormal_interp; #endif -#ifdef ENABLE_COLOR_INTERP +#if defined(ENABLE_COLOR_INTERP) varying vec4 color_interp; #endif -#ifdef ENABLE_UV_INTERP +#if defined(ENABLE_UV_INTERP) varying vec2 uv_interp; #endif -#ifdef ENABLE_UV2_INTERP +#if defined(ENABLE_UV2_INTERP) || defined(USE_LIGHTMAP) varying vec2 uv2_interp; #endif @@ -971,14 +1215,17 @@ LIGHT_SHADER_CODE #ifdef USE_SHADOW #define SAMPLE_SHADOW_TEXEL(p_shadow, p_pos, p_depth) step(p_depth, texture2D(p_shadow, p_pos).r) +#define SAMPLE_SHADOW_TEXEL_PROJ(p_shadow, p_pos) step(p_pos.z, texture2DProj(p_shadow, p_pos).r) float sample_shadow( - highp sampler2D shadow, - highp vec2 pos, - highp float depth) { + highp sampler2D shadow, highp vec4 spos) { #ifdef SHADOW_MODE_PCF_13 + spos.xyz /= spos.w; + vec2 pos = spos.xy; + float depth = spos.z; + float avg = SAMPLE_SHADOW_TEXEL(shadow, pos, depth); avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x, 0.0), depth); avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x, 0.0), depth); @@ -997,6 +1244,10 @@ float sample_shadow( #ifdef SHADOW_MODE_PCF_5 + spos.xyz /= spos.w; + vec2 pos = spos.xy; + float depth = spos.z; + float avg = SAMPLE_SHADOW_TEXEL(shadow, pos, depth); avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(shadow_pixel_size.x, 0.0), depth); avg += SAMPLE_SHADOW_TEXEL(shadow, pos + vec2(-shadow_pixel_size.x, 0.0), depth); @@ -1008,7 +1259,7 @@ float sample_shadow( #if !defined(SHADOW_MODE_PCF_5) || !defined(SHADOW_MODE_PCF_13) - return SAMPLE_SHADOW_TEXEL(shadow, pos, depth); + return SAMPLE_SHADOW_TEXEL_PROJ(shadow, spos); #endif } @@ -1034,6 +1285,7 @@ void main() { float clearcoat_gloss = 0.0; float anisotropy = 0.0; vec2 anisotropy_flow = vec2(1.0, 0.0); + float sss_strength = 0.0; //unused float alpha = 1.0; float side = 1.0; @@ -1057,11 +1309,11 @@ void main() { #endif float normaldepth = 1.0; -#ifdef ALPHA_SCISSOR_USED +#if defined(ALPHA_SCISSOR_USED) float alpha_scissor = 0.5; #endif -#ifdef SCREEN_UV_USED +#if defined(SCREEN_UV_USED) vec2 screen_uv = gl_FragCoord.xy * screen_pixel_size; #endif @@ -1077,8 +1329,8 @@ FRAGMENT_SHADER_CODE normalmap.xy = normalmap.xy * 2.0 - 1.0; normalmap.z = sqrt(max(0.0, 1.0 - dot(normalmap.xy, normalmap.xy))); - // normal = normalize(mix(normal_interp, tangent * normalmap.x + binormal * normalmap.y + normal * normalmap.z, normaldepth)) * side; - normal = normalmap; + normal = normalize(mix(normal_interp, tangent * normalmap.x + binormal * normalmap.y + normal * normalmap.z, normaldepth)) * side; + //normal = normalmap; #endif normal = normalize(normal); @@ -1091,7 +1343,7 @@ FRAGMENT_SHADER_CODE vec3 eye_position = -normalize(vertex_interp); -#ifdef ALPHA_SCISSOR_USED +#if defined(ALPHA_SCISSOR_USED) if (alpha < alpha_scissor) { discard; } @@ -1123,6 +1375,99 @@ FRAGMENT_SHADER_CODE ambient_light *= ambient_energy; +#if defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2) + + vec4 ambient_accum = vec4(0.0); + vec4 reflection_accum = vec4(0.0); + +#ifdef USE_REFLECTION_PROBE1 + + reflection_process(reflection_probe1, +#ifdef USE_VERTEX_LIGHTING + refprobe1_reflection_normal_blend.rgb, +#ifndef USE_LIGHTMAP + refprobe1_ambient_normal, +#endif + refprobe1_reflection_normal_blend.a, +#else + normal_interp, vertex_interp, refprobe1_local_matrix, + refprobe1_use_box_project, refprobe1_box_extents, refprobe1_box_offset, +#endif + refprobe1_exterior, refprobe1_intensity, refprobe1_ambient, roughness, + ambient_light, specular_light, reflection_accum, ambient_accum); + +#endif // USE_REFLECTION_PROBE1 + +#ifdef USE_REFLECTION_PROBE2 + + reflection_process(reflection_probe2, +#ifdef USE_VERTEX_LIGHTING + refprobe2_reflection_normal_blend.rgb, +#ifndef USE_LIGHTMAP + refprobe2_ambient_normal, +#endif + refprobe2_reflection_normal_blend.a, +#else + normal_interp, vertex_interp, refprobe2_local_matrix, + refprobe2_use_box_project, refprobe2_box_extents, refprobe2_box_offset, +#endif + refprobe2_exterior, refprobe2_intensity, refprobe2_ambient, roughness, + ambient_light, specular_light, reflection_accum, ambient_accum); + +#endif // USE_REFLECTION_PROBE2 + + if (reflection_accum.a > 0.0) { + specular_light = reflection_accum.rgb / reflection_accum.a; + } + +#ifndef USE_LIGHTMAP + if (ambient_accum.a > 0.0) { + ambient_light = ambient_accum.rgb / ambient_accum.a; + } +#endif + +#endif // defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2) + +#ifdef USE_LIGHTMAP + //ambient light will come entirely from lightmap is lightmap is used + ambient_light = texture2D(lightmap, uv2_interp).rgb * lightmap_energy; +#endif + +#ifdef USE_LIGHTMAP_CAPTURE + { + vec3 cone_dirs[12] = vec3[]( + vec3(0, 0, 1), + vec3(0.866025, 0, 0.5), + vec3(0.267617, 0.823639, 0.5), + vec3(-0.700629, 0.509037, 0.5), + vec3(-0.700629, -0.509037, 0.5), + vec3(0.267617, -0.823639, 0.5), + vec3(0, 0, -1), + vec3(0.866025, 0, -0.5), + vec3(0.267617, 0.823639, -0.5), + vec3(-0.700629, 0.509037, -0.5), + vec3(-0.700629, -0.509037, -0.5), + vec3(0.267617, -0.823639, -0.5)); + + vec3 local_normal = normalize(camera_matrix * vec4(normal, 0.0)).xyz; + vec4 captured = vec4(0.0); + float sum = 0.0; + for (int i = 0; i < 12; i++) { + float amount = max(0.0, dot(local_normal, cone_dirs[i])); //not correct, but creates a nice wrap around effect + captured += lightmap_captures[i] * amount; + sum += amount; + } + + captured /= sum; + + if (lightmap_capture_sky) { + ambient_light = mix(ambient_light, captured.rgb, captured.a); + } else { + ambient_light = captured.rgb; + } + } +#endif + #endif //BASE PASS // @@ -1142,20 +1487,24 @@ FRAGMENT_SHADER_CODE float light_length = length(light_vec); float normalized_distance = light_length / light_range; + if (normalized_distance < 1.0) { - float omni_attenuation = pow(1.0 - normalized_distance, light_attenuation.w); + float omni_attenuation = pow(1.0 - normalized_distance, light_attenuation); - light_att = vec3(omni_attenuation); + light_att = vec3(omni_attenuation); + } else { + light_att = vec3(0.0); + } L = normalize(light_vec); #endif #ifdef USE_SHADOW { - highp vec3 splane = shadow_coord.xyz; - float shadow_len = length(splane); + highp vec4 splane = shadow_coord; + float shadow_len = length(splane.xyz); - splane = normalize(splane); + splane = normalize(splane.xyz); vec4 clamp_rect = light_clamp; @@ -1172,8 +1521,9 @@ FRAGMENT_SHADER_CODE splane.z = shadow_len / light_range; splane.xy = clamp_rect.xy + splane.xy * clamp_rect.zw; + splane.w = 1.0; - float shadow = sample_shadow(light_shadow_atlas, splane.xy, splane.z); + float shadow = sample_shadow(light_shadow_atlas, splane); light_att *= shadow; } @@ -1190,6 +1540,121 @@ FRAGMENT_SHADER_CODE float depth_z = -vertex.z; #ifdef USE_SHADOW + +#ifdef USE_VERTEX_LIGHTING + //compute shadows in a mobile friendly way + +#ifdef LIGHT_USE_PSSM4 + //take advantage of prefetch + float shadow1 = sample_shadow(light_directional_shadow, shadow_coord); + float shadow2 = sample_shadow(light_directional_shadow, shadow_coord2); + float shadow3 = sample_shadow(light_directional_shadow, shadow_coord3); + float shadow4 = sample_shadow(light_directional_shadow, shadow_coord4); + + if (depth_z < light_split_offsets.w) { + float pssm_fade = 0.0; + float shadow_att = 1.0; +#ifdef LIGHT_USE_PSSM_BLEND + float shadow_att2 = 1.0; + float pssm_blend = 0.0; + bool use_blend = true; +#endif + if (depth_z < light_split_offsets.y) { + if (depth_z < light_split_offsets.x) { + shadow_att = shadow1; + +#ifdef LIGHT_USE_PSSM_BLEND + shadow_att2 = shadow2; + + pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); +#endif + } else { + shadow_att = shadow2; + +#ifdef LIGHT_USE_PSSM_BLEND + shadow_att2 = shadow3; + + pssm_blend = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); +#endif + } + } else { + if (depth_z < light_split_offsets.z) { + + shadow_att = shadow3; + +#if defined(LIGHT_USE_PSSM_BLEND) + shadow_att2 = shadow4; + pssm_blend = smoothstep(light_split_offsets.y, light_split_offsets.z, depth_z); +#endif + + } else { + + shadow_att = shadow4; + pssm_fade = smoothstep(light_split_offsets.z, light_split_offsets.w, depth_z); + +#if defined(LIGHT_USE_PSSM_BLEND) + use_blend = false; +#endif + } + } +#if defined(LIGHT_USE_PSSM_BLEND) + if (use_blend) { + shadow_att = mix(shadow_att, shadow_att2, pssm_blend); + } +#endif + light_att *= shadow_att; + } + +#endif //LIGHT_USE_PSSM4 + +#ifdef LIGHT_USE_PSSM2 + + //take advantage of prefetch + float shadow1 = sample_shadow(light_directional_shadow, shadow_coord); + float shadow2 = sample_shadow(light_directional_shadow, shadow_coord2); + + if (depth_z < light_split_offsets.y) { + float shadow_att = 1.0; + float pssm_fade = 0.0; + +#ifdef LIGHT_USE_PSSM_BLEND + float shadow_att2 = 1.0; + float pssm_blend = 0.0; + bool use_blend = true; +#endif + if (depth_z < light_split_offsets.x) { + float pssm_fade = 0.0; + shadow_att = shadow1; + +#ifdef LIGHT_USE_PSSM_BLEND + shadow_att2 = shadow2; + pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); +#endif + } else { + + shadow_att = shadow2; + pssm_fade = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); +#ifdef LIGHT_USE_PSSM_BLEND + use_blend = false; +#endif + } +#ifdef LIGHT_USE_PSSM_BLEND + if (use_blend) { + shadow_att = mix(shadow_att, shadow_att2, pssm_blend); + } +#endif + light_att *= shadow_att; + } + +#endif //LIGHT_USE_PSSM2 + +#if !defined(LIGHT_USE_PSSM4) && !defined(LIGHT_USE_PSSM2) + + light_att *= sample_shadow(light_directional_shadow, shadow_coord); +#endif //orthogonal + +#else //fragment version of pssm + { #ifdef LIGHT_USE_PSSM4 if (depth_z < light_split_offsets.w) { @@ -1199,34 +1664,31 @@ FRAGMENT_SHADER_CODE if (depth_z < light_split_offsets.x) { #endif //pssm2 - vec3 pssm_coord; + highp vec4 pssm_coord; float pssm_fade = 0.0; #ifdef LIGHT_USE_PSSM_BLEND float pssm_blend; - vec3 pssm_coord2; + highp vec4 pssm_coord2; bool use_blend = true; #endif #ifdef LIGHT_USE_PSSM4 + if (depth_z < light_split_offsets.y) { if (depth_z < light_split_offsets.x) { - highp vec4 splane = shadow_coord; - pssm_coord = splane.xyz / splane.w; + pssm_coord = shadow_coord; #ifdef LIGHT_USE_PSSM_BLEND - splane = shadow_coord2; - pssm_coord2 = splane.xyz / splane.w; + pssm_coord2 = shadow_coord2; pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); #endif } else { - highp vec4 splane = shadow_coord2; - pssm_coord = splane.xyz / splane.w; + pssm_coord = shadow_coord2; #ifdef LIGHT_USE_PSSM_BLEND - splane = shadow_coord3; - pssm_coord2 = splane.xyz / splane.w; + pssm_coord2 = shadow_coord3; pssm_blend = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); #endif @@ -1234,19 +1696,16 @@ FRAGMENT_SHADER_CODE } else { if (depth_z < light_split_offsets.z) { - highp vec4 splane = shadow_coord3; - pssm_coord = splane.xyz / splane.w; + pssm_coord = shadow_coord3; #if defined(LIGHT_USE_PSSM_BLEND) - splane = shadow_coord4; - pssm_coord2 = splane.xyz / splane.w; + pssm_coord2 = shadow_coord4; pssm_blend = smoothstep(light_split_offsets.y, light_split_offsets.z, depth_z); #endif } else { - highp vec4 splane = shadow_coord4; - pssm_coord = splane.xyz / splane.w; + pssm_coord = shadow_coord4; pssm_fade = smoothstep(light_split_offsets.z, light_split_offsets.w, depth_z); #if defined(LIGHT_USE_PSSM_BLEND) @@ -1260,17 +1719,15 @@ FRAGMENT_SHADER_CODE #ifdef LIGHT_USE_PSSM2 if (depth_z < light_split_offsets.x) { - highp vec4 splane = shadow_coord; - pssm_coord = splane.xyz / splane.w; + pssm_coord = shadow_coord; #ifdef LIGHT_USE_PSSM_BLEND - splane = shadow_coord2; - pssm_coord2 = splane.xyz / splane.w; + pssm_coord2 = shadow_coord2; pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); #endif } else { - highp vec4 splane = shadow_coord2; - pssm_coord = splane.xyz / splane.w; + + pssm_coord = shadow_coord2; pssm_fade = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); #ifdef LIGHT_USE_PSSM_BLEND use_blend = false; @@ -1281,22 +1738,23 @@ FRAGMENT_SHADER_CODE #if !defined(LIGHT_USE_PSSM4) && !defined(LIGHT_USE_PSSM2) { - highp vec4 splane = shadow_coord; - pssm_coord = splane.xyz / splane.w; + pssm_coord = shadow_coord; } #endif - float shadow = sample_shadow(light_directional_shadow, pssm_coord.xy, pssm_coord.z); + float shadow = sample_shadow(light_directional_shadow, pssm_coord); #ifdef LIGHT_USE_PSSM_BLEND if (use_blend) { - shadow = mix(shadow, sample_shadow(light_directional_shadow, pssm_coord2.xy, pssm_coord2.z), pssm_blend); + shadow = mix(shadow, sample_shadow(light_directional_shadow, pssm_coord2), pssm_blend); } #endif light_att *= shadow; } } +#endif //use vertex lighting + #endif //use shadow #endif @@ -1311,17 +1769,25 @@ FRAGMENT_SHADER_CODE float light_length = length(light_rel_vec); float normalized_distance = light_length / light_range; - float spot_attenuation = pow(1.0 - normalized_distance, light_attenuation.w); - vec3 spot_dir = light_direction; + if (normalized_distance < 1.0) { + float spot_attenuation = pow(1.0 - normalized_distance, light_attenuation); + vec3 spot_dir = light_direction; - float spot_cutoff = light_spot_angle; + float spot_cutoff = light_spot_angle; + float angle = dot(-normalize(light_rel_vec), spot_dir); - float scos = max(dot(-normalize(light_rel_vec), spot_dir), spot_cutoff); - float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_cutoff)); + if (angle > spot_cutoff) { + float scos = max(angle, spot_cutoff); + float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_cutoff)); + spot_attenuation *= 1.0 - pow(spot_rim, light_spot_attenuation); - spot_attenuation *= 1.0 - pow(spot_rim, light_spot_attenuation); - - light_att = vec3(spot_attenuation); + light_att = vec3(spot_attenuation); + } else { + light_att = vec3(0.0); + } + } else { + light_att = vec3(0.0); + } L = normalize(light_rel_vec); @@ -1394,8 +1860,13 @@ FRAGMENT_SHADER_CODE // environment BRDF approximation - // TODO shadeless { + +#if defined(DIFFUSE_TOON) + //simplify for toon, as + specular_light *= specular * metallic * albedo * 2.0; +#else + //TODO: this curve is not really designed for gammaspace, should be adjusted const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022); const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04); vec4 r = roughness * c0 + c1; @@ -1405,9 +1876,15 @@ FRAGMENT_SHADER_CODE vec3 specular_color = metallic_to_specular_color(metallic, specular, albedo); specular_light *= AB.x * specular_color + AB.y; +#endif } gl_FragColor = vec4(ambient_light + diffuse_light + specular_light, alpha); + + //add emission if in base pass +#ifdef BASE_PASS + gl_FragColor.rgb += emission; +#endif // gl_FragColor = vec4(normal, 1.0); #endif //unshaded diff --git a/drivers/gles2/shaders/stdlib.glsl b/drivers/gles2/shaders/stdlib.glsl index 6bc81a22d8..3674d70c9f 100644 --- a/drivers/gles2/shaders/stdlib.glsl +++ b/drivers/gles2/shaders/stdlib.glsl @@ -35,3 +35,13 @@ highp vec4 texel2DFetch(highp sampler2D tex, ivec2 size, ivec2 coord) { return texture2DLod(tex, vec2(x_coord, y_coord), 0.0); } + +#ifndef USE_GLES_OVER_GL +highp mat4 transpose(highp mat4 src) { + return mat4( + vec4(src[0].x, src[1].x, src[2].x, src[3].x), + vec4(src[0].y, src[1].y, src[2].y, src[3].y), + vec4(src[0].z, src[1].z, src[2].z, src[3].z), + vec4(src[0].w, src[1].w, src[2].w, src[3].w)); +} +#endif diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index c9bdc6f5c3..856c83e297 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -1223,8 +1223,6 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons bool rebind_shader = true; - Size2 rt_size = Size2(storage->frame.current_rt->width, storage->frame.current_rt->height); - state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_DISTANCE_FIELD, false); glBindBuffer(GL_UNIFORM_BUFFER, state.canvas_item_ubo); diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index d3f6dcd497..6f13df621f 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -32,7 +32,7 @@ #include "core/os/os.h" #include "core/project_settings.h" -#include "gl_context/context_gl.h" +#include "drivers/gl_context/context_gl.h" RasterizerStorage *RasterizerGLES3::get_storage() { diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index 0a264caf8f..543011aff3 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -66,6 +66,8 @@ public: static void make_current(); static void register_config(); + virtual bool is_low_end() const { return false; } + RasterizerGLES3(); ~RasterizerGLES3(); }; diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 086829f9ba..7160668fe8 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -51,26 +51,6 @@ static const GLenum _cube_side_enum[6] = { }; -static _FORCE_INLINE_ void store_transform2d(const Transform2D &p_mtx, float *p_array) { - - p_array[0] = p_mtx.elements[0][0]; - p_array[1] = p_mtx.elements[0][1]; - p_array[2] = 0; - p_array[3] = 0; - p_array[4] = p_mtx.elements[1][0]; - p_array[5] = p_mtx.elements[1][1]; - p_array[6] = 0; - p_array[7] = 0; - p_array[8] = 0; - p_array[9] = 0; - p_array[10] = 1; - p_array[11] = 0; - p_array[12] = p_mtx.elements[2][0]; - p_array[13] = p_mtx.elements[2][1]; - p_array[14] = 0; - p_array[15] = 1; -} - static _FORCE_INLINE_ void store_transform(const Transform &p_mtx, float *p_array) { p_array[0] = p_mtx.basis.elements[0][0]; p_array[1] = p_mtx.basis.elements[1][0]; @@ -1202,7 +1182,7 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m glActiveTexture(GL_TEXTURE0 + i); - GLenum target; + GLenum target = GL_TEXTURE_2D; GLuint tex = 0; RasterizerStorageGLES3::Texture *t = storage->texture_owner.getptr(textures[i]); @@ -1282,6 +1262,8 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m case ShaderLanguage::TYPE_SAMPLER2DARRAY: { // TODO } break; + + default: {} } } @@ -1509,6 +1491,7 @@ void RasterizerSceneGLES3::_setup_geometry(RenderList::Element *e, const Transfo } } break; + default: {} } } @@ -1830,6 +1813,7 @@ void RasterizerSceneGLES3::_render_geometry(RenderList::Element *e) { } } break; + default: {} } } @@ -3036,13 +3020,14 @@ void RasterizerSceneGLES3::_setup_reflections(RID *p_reflection_probe_cull_resul reflection_ubo.ambient[3] = rpi->probe_ptr->interior_ambient_probe_contrib; } else { Color ambient_linear; - float contrib = 0; + // FIXME: contrib was retrieved but never used, is it meant to be set as ambient[3]? (GH-20361) + //float contrib = 0; if (p_env) { ambient_linear = p_env->ambient_color.to_linear(); ambient_linear.r *= p_env->ambient_energy; ambient_linear.g *= p_env->ambient_energy; ambient_linear.b *= p_env->ambient_energy; - contrib = p_env->ambient_sky_contribution; + //contrib = p_env->ambient_sky_contribution; } reflection_ubo.ambient[0] = ambient_linear.r; @@ -3209,6 +3194,7 @@ void RasterizerSceneGLES3::_fill_render_list(InstanceBase **p_cull_result, int p } } break; + default: {} } } } @@ -4295,7 +4281,6 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const if (env) { switch (env->bg_mode) { case VS::ENV_BG_COLOR_SKY: - case VS::ENV_BG_SKY: sky = storage->sky_owner.getornull(env->sky); @@ -4333,6 +4318,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); break; + default: {} } } @@ -4501,7 +4487,7 @@ void RasterizerSceneGLES3::render_shadow(RID p_light, RID p_shadow_atlas, int p_ RasterizerStorageGLES3::Light *light = storage->light_owner.getornull(light_instance->light); ERR_FAIL_COND(!light); - uint32_t x, y, width, height, vp_height; + uint32_t x, y, width, height; float dp_direction = 0.0; float zfar = 0; @@ -4583,7 +4569,6 @@ void RasterizerSceneGLES3::render_shadow(RID p_light, RID p_shadow_atlas, int p_ bias = light->param[VS::LIGHT_PARAM_SHADOW_BIAS] * bias_mult; normal_bias = light->param[VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS] * bias_mult; fbo = directional_shadow.fbo; - vp_height = directional_shadow.size; } else { //set from shadow atlas @@ -4593,7 +4578,6 @@ void RasterizerSceneGLES3::render_shadow(RID p_light, RID p_shadow_atlas, int p_ ERR_FAIL_COND(!shadow_atlas->shadow_owners.has(p_light)); fbo = shadow_atlas->fbo; - vp_height = shadow_atlas->size; uint32_t key = shadow_atlas->shadow_owners[p_light]; diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index 25e7bd0424..797441c3a1 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -1029,7 +1029,7 @@ Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, int p_layer) PoolVector<uint8_t> data; - int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1 ? -1 : 0); + int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1); data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers PoolVector<uint8_t>::Write wb = data.write(); @@ -1072,7 +1072,7 @@ Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, int p_layer) uint32_t *ptr = (uint32_t *)wb.ptr(); uint32_t num_pixels = data_size / 4; - for (int ofs = 0; ofs < num_pixels; ofs++) { + for (uint32_t ofs = 0; ofs < num_pixels; ofs++) { uint32_t px = ptr[ofs]; uint32_t a = px >> 30 & 0xFF; @@ -1905,6 +1905,7 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const { actions = &shaders.actions_particles; actions->uniforms = &p_shader->uniforms; } break; + case VS::SHADER_MAX: break; // Can't happen, but silences warning } Error err = shaders.compiler.compile(p_shader->mode, p_shader->code, actions, p_shader->path, gen_code); @@ -2028,6 +2029,14 @@ void RasterizerStorageGLES3::shader_get_param_list(RID p_shader, List<PropertyIn pi.hint = PROPERTY_HINT_RESOURCE_TYPE; pi.hint_string = "Texture"; } break; + case ShaderLanguage::TYPE_SAMPLER2DARRAY: + case ShaderLanguage::TYPE_ISAMPLER2DARRAY: + case ShaderLanguage::TYPE_USAMPLER2DARRAY: { + + pi.type = Variant::OBJECT; + pi.hint = PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string = "TextureArray"; + } break; case ShaderLanguage::TYPE_SAMPLER3D: case ShaderLanguage::TYPE_ISAMPLER3D: case ShaderLanguage::TYPE_USAMPLER3D: { @@ -4961,6 +4970,7 @@ void RasterizerStorageGLES3::light_set_param(RID p_light, VS::LightParam p_param light->version++; light->instance_change_notify(); } break; + default: {} } light->param[p_param] = p_value; @@ -5285,6 +5295,9 @@ void RasterizerStorageGLES3::reflection_probe_set_cull_mask(RID p_probe, uint32_ reflection_probe->instance_change_notify(); } +void RasterizerStorageGLES3::reflection_probe_set_resolution(RID p_probe, int p_resolution) { +} + AABB RasterizerStorageGLES3::reflection_probe_get_aabb(RID p_probe) const { const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe); ERR_FAIL_COND_V(!reflection_probe, AABB()); @@ -7351,7 +7364,7 @@ bool RasterizerStorageGLES3::free(RID p_rid) { GIProbeData *gi_probe_data = gi_probe_data_owner.get(p_rid); glDeleteTextures(1, &gi_probe_data->tex_id); - gi_probe_owner.free(p_rid); + gi_probe_data_owner.free(p_rid); memdelete(gi_probe_data); } else if (lightmap_capture_data_owner.owns(p_rid)) { @@ -7359,7 +7372,7 @@ bool RasterizerStorageGLES3::free(RID p_rid) { LightmapCapture *lightmap_capture = lightmap_capture_data_owner.get(p_rid); lightmap_capture->instance_remove_deps(); - gi_probe_owner.free(p_rid); + lightmap_capture_data_owner.free(p_rid); memdelete(lightmap_capture); } else if (canvas_occluder_owner.owns(p_rid)) { diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h index 0bd9c22be5..9a4798ac2a 100644 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ b/drivers/gles3/rasterizer_storage_gles3.h @@ -691,7 +691,7 @@ public: } }; - class MultiMesh; + struct MultiMesh; struct Mesh : public GeometryOwner { @@ -1005,6 +1005,7 @@ public: virtual void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable); virtual void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable); virtual void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers); + virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution); virtual AABB reflection_probe_get_aabb(RID p_probe) const; virtual VS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const; diff --git a/drivers/gles3/shader_compiler_gles3.cpp b/drivers/gles3/shader_compiler_gles3.cpp index 350107de69..dbc8507951 100644 --- a/drivers/gles3/shader_compiler_gles3.cpp +++ b/drivers/gles3/shader_compiler_gles3.cpp @@ -79,6 +79,12 @@ static int _get_datatype_size(SL::DataType p_type) { case SL::TYPE_SAMPLER2D: return 16; case SL::TYPE_ISAMPLER2D: return 16; case SL::TYPE_USAMPLER2D: return 16; + case SL::TYPE_SAMPLER2DARRAY: return 16; + case SL::TYPE_ISAMPLER2DARRAY: return 16; + case SL::TYPE_USAMPLER2DARRAY: return 16; + case SL::TYPE_SAMPLER3D: return 16; + case SL::TYPE_ISAMPLER3D: return 16; + case SL::TYPE_USAMPLER3D: return 16; case SL::TYPE_SAMPLERCUBE: return 16; } @@ -112,6 +118,12 @@ static int _get_datatype_alignment(SL::DataType p_type) { case SL::TYPE_SAMPLER2D: return 16; case SL::TYPE_ISAMPLER2D: return 16; case SL::TYPE_USAMPLER2D: return 16; + case SL::TYPE_SAMPLER2DARRAY: return 16; + case SL::TYPE_ISAMPLER2DARRAY: return 16; + case SL::TYPE_USAMPLER2DARRAY: return 16; + case SL::TYPE_SAMPLER3D: return 16; + case SL::TYPE_ISAMPLER3D: return 16; + case SL::TYPE_USAMPLER3D: return 16; case SL::TYPE_SAMPLERCUBE: return 16; } diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 7da20dfa00..bcaf4a57a8 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -1136,8 +1136,9 @@ float sample_shadow(highp sampler2DShadow shadow, vec2 shadow_pixel_size, vec2 p avg += textureProj(shadow, vec4(pos + vec2(0.0, shadow_pixel_size.y * 2.0), depth, 1.0)); avg += textureProj(shadow, vec4(pos + vec2(0.0, -shadow_pixel_size.y * 2.0), depth, 1.0)); return avg * (1.0 / 13.0); +#endif -#elif defined(SHADOW_MODE_PCF_5) +#ifdef SHADOW_MODE_PCF_5 float avg = textureProj(shadow, vec4(pos, depth, 1.0)); avg += textureProj(shadow, vec4(pos + vec2(shadow_pixel_size.x, 0.0), depth, 1.0)); @@ -1146,7 +1147,9 @@ float sample_shadow(highp sampler2DShadow shadow, vec2 shadow_pixel_size, vec2 p avg += textureProj(shadow, vec4(pos + vec2(0.0, -shadow_pixel_size.y), depth, 1.0)); return avg * (1.0 / 5.0); -#else +#endif + +#if !defined(SHADOW_MODE_PCF_5) || !defined(SHADOW_MODE_PCF_13) return textureProj(shadow, vec4(pos, depth, 1.0)); diff --git a/drivers/png/SCsub b/drivers/png/SCsub index 39480351a6..22fb1817d1 100644 --- a/drivers/png/SCsub +++ b/drivers/png/SCsub @@ -26,14 +26,24 @@ if env['builtin_libpng']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_png.add_source_files(env.drivers_sources, thirdparty_sources) env_png.Append(CPPPATH=[thirdparty_dir]) + # Needed for drivers includes and in platform/javascript + env.Append(CPPPATH=[thirdparty_dir]) # Currently .ASM filter_neon.S does not compile on NT. import os - if ("neon_enabled" in env and env["neon_enabled"]) and os.name != "nt": + use_neon = "neon_enabled" in env and env["neon_enabled"] and os.name != "nt" + if use_neon: env_png.Append(CPPFLAGS=["-DPNG_ARM_NEON_OPT=2"]) - env_neon = env_png.Clone() + else: + env_png.Append(CPPFLAGS=["-DPNG_ARM_NEON_OPT=0"]) + + env_thirdparty = env_png.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.drivers_sources, thirdparty_sources) + + if use_neon: + env_neon = env_thirdparty.Clone() if "S_compiler" in env: env_neon['CC'] = env['S_compiler'] neon_sources = [] @@ -41,8 +51,6 @@ if env['builtin_libpng']: neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon_intrinsics.c")) neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon.S")) env.drivers_sources += neon_sources - else: - env_png.Append(CPPFLAGS=["-DPNG_ARM_NEON_OPT=0"]) # Godot source files env_png.add_source_files(env.drivers_sources, "*.cpp") diff --git a/drivers/pulseaudio/SCsub b/drivers/pulseaudio/SCsub index ee39fd2631..28b315ae66 100644 --- a/drivers/pulseaudio/SCsub +++ b/drivers/pulseaudio/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.drivers_sources, "*.cpp") - -Export('env') diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp index 7578fbc0a0..9c02549e39 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp +++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp @@ -43,10 +43,13 @@ void AudioDriverPulseAudio::pa_state_cb(pa_context *c, void *userdata) { case PA_CONTEXT_FAILED: ad->pa_ready = -1; break; - case PA_CONTEXT_READY: ad->pa_ready = 1; break; + default: + // TODO: Check if we want to handle some of the other + // PA context states like PA_CONTEXT_UNCONNECTED. + break; } } @@ -340,7 +343,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { unsigned int out_idx = 0; for (unsigned int i = 0; i < ad->buffer_frames; i++) { - for (unsigned int j = 0; j < ad->pa_map.channels - 1; j++) { + for (int j = 0; j < ad->pa_map.channels - 1; j++) { ad->samples_out.write[out_idx++] = ad->samples_in[in_idx++] >> 16; } uint32_t l = ad->samples_in[in_idx++]; diff --git a/drivers/register_driver_types.cpp b/drivers/register_driver_types.cpp index c6d36a5749..9f5d9c1abf 100644 --- a/drivers/register_driver_types.cpp +++ b/drivers/register_driver_types.cpp @@ -31,11 +31,11 @@ #include "register_driver_types.h" #include "core/math/geometry.h" -#include "png/image_loader_png.h" -#include "png/resource_saver_png.h" +#include "drivers/png/image_loader_png.h" +#include "drivers/png/resource_saver_png.h" #ifdef TOOLS_ENABLED -#include "convex_decomp/b2d_decompose.h" +#include "drivers/convex_decomp/b2d_decompose.h" #endif #ifdef TOOLS_ENABLED diff --git a/drivers/rtaudio/SCsub b/drivers/rtaudio/SCsub index 2b0a602965..285658073c 100644 --- a/drivers/rtaudio/SCsub +++ b/drivers/rtaudio/SCsub @@ -11,9 +11,12 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env.add_source_files(env.drivers_sources, thirdparty_sources) env.Append(CPPPATH=[thirdparty_dir]) +env_thirdparty = env.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.drivers_sources, thirdparty_sources) + # Driver source files env.add_source_files(env.drivers_sources, "*.cpp") diff --git a/drivers/unix/SCsub b/drivers/unix/SCsub index ada8255580..4888f56099 100644 --- a/drivers/unix/SCsub +++ b/drivers/unix/SCsub @@ -5,5 +5,3 @@ Import('env') env.add_source_files(env.drivers_sources, "*.cpp") env["check_c_headers"] = [ [ "mntent.h", "HAVE_MNTENT" ] ] - -Export('env') diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_posix.cpp index 6e0bf97711..3f03175403 100644 --- a/drivers/unix/net_socket_posix.cpp +++ b/drivers/unix/net_socket_posix.cpp @@ -94,7 +94,7 @@ #endif -static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IP_Address &p_ip, uint16_t p_port, IP::Type p_ip_type) { +size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const IP_Address &p_ip, uint16_t p_port, IP::Type p_ip_type) { memset(p_addr, 0, sizeof(struct sockaddr_storage)); if (p_ip_type == IP::TYPE_IPV6 || p_ip_type == IP::TYPE_ANY) { // IPv6 socket @@ -126,12 +126,12 @@ static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IP_Addres addr4->sin_addr.s_addr = INADDR_ANY; } - copymem(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 16); + copymem(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 4); return sizeof(sockaddr_in); } } -static void _set_ip_port(IP_Address &r_ip, uint16_t &r_port, struct sockaddr_storage *p_addr) { +void NetSocketPosix::_set_ip_port(struct sockaddr_storage *p_addr, IP_Address &r_ip, uint16_t &r_port) { if (p_addr->ss_family == AF_INET) { @@ -559,7 +559,7 @@ void NetSocketPosix::set_ipv6_only_enabled(bool p_enabled) { void NetSocketPosix::set_tcp_no_delay_enabled(bool p_enabled) { ERR_FAIL_COND(!is_open()); - ERR_FAIL_COND(_ip_type != TYPE_TCP); + ERR_FAIL_COND(!_is_stream); // Not TCP int par = p_enabled ? 1 : 0; if (setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, SOCK_CBUF(&par), sizeof(int)) < 0) { @@ -612,7 +612,7 @@ Ref<NetSocket> NetSocketPosix::accept(IP_Address &r_ip, uint16_t &r_port) { SOCKET_TYPE fd = ::accept(_sock, (struct sockaddr *)&their_addr, &size); ERR_FAIL_COND_V(fd == SOCK_EMPTY, out); - _set_ip_port(r_ip, r_port, &their_addr); + _set_ip_port(&their_addr, r_ip, r_port); NetSocketPosix *ns = memnew(NetSocketPosix); ns->_set_socket(fd, _ip_type, _is_stream); diff --git a/drivers/unix/net_socket_posix.h b/drivers/unix/net_socket_posix.h index 8177e01987..010f2ea6e0 100644 --- a/drivers/unix/net_socket_posix.h +++ b/drivers/unix/net_socket_posix.h @@ -39,6 +39,7 @@ #define SOCKET_TYPE SOCKET #else +#include <sys/socket.h> #define SOCKET_TYPE int #endif @@ -68,6 +69,8 @@ protected: public: static void make_default(); static void cleanup(); + static void _set_ip_port(struct sockaddr_storage *p_addr, IP_Address &r_ip, uint16_t &r_port); + static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IP_Address &p_ip, uint16_t p_port, IP::Type p_ip_type); virtual Error open(Type p_sock_type, IP::Type &ip_type); virtual void close(); diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index 9936c95cf9..6c70934bc6 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -45,6 +45,7 @@ #ifdef __APPLE__ #include <mach-o/dyld.h> +#include <mach/mach_time.h> #endif #if defined(__FreeBSD__) || defined(__OpenBSD__) @@ -64,6 +65,32 @@ #include <sys/wait.h> #include <unistd.h> +/// Clock Setup function (used by get_ticks_usec) +static uint64_t _clock_start = 0; +#if defined(__APPLE__) +static double _clock_scale = 0; +static void _setup_clock() { + mach_timebase_info_data_t info; + kern_return_t ret = mach_timebase_info(&info); + ERR_EXPLAIN("OS CLOCK IS NOT WORKING!"); + ERR_FAIL_COND(ret != 0); + _clock_scale = ((double)info.numer / (double)info.denom) / 1000.0; + _clock_start = mach_absolute_time() * _clock_scale; +} +#else +#if defined(CLOCK_MONOTONIC_RAW) && !defined(JAVASCRIPT_ENABLED) // This is a better clock on Linux. +#define GODOT_CLOCK CLOCK_MONOTONIC_RAW +#else +#define GODOT_CLOCK CLOCK_MONOTONIC +#endif +static void _setup_clock() { + struct timespec tv_now = { 0, 0 }; + ERR_EXPLAIN("OS CLOCK IS NOT WORKING!"); + ERR_FAIL_COND(clock_gettime(GODOT_CLOCK, &tv_now) != 0); + _clock_start = ((uint64_t)tv_now.tv_nsec / 1000L) + (uint64_t)tv_now.tv_sec * 1000000L; +} +#endif + void OS_Unix::debug_break() { assert(false); @@ -126,8 +153,7 @@ void OS_Unix::initialize_core() { IP_Unix::make_default(); #endif - ticks_start = 0; - ticks_start = get_ticks_usec(); + _setup_clock(); struct sigaction sa; sa.sa_handler = &handle_sigchld; @@ -246,11 +272,16 @@ void OS_Unix::delay_usec(uint32_t p_usec) const { } uint64_t OS_Unix::get_ticks_usec() const { - struct timeval tv_now; - gettimeofday(&tv_now, NULL); - - uint64_t longtime = (uint64_t)tv_now.tv_usec + (uint64_t)tv_now.tv_sec * 1000000L; - longtime -= ticks_start; +#if defined(__APPLE__) + uint64_t longtime = mach_absolute_time() * _clock_scale; +#else + // Unchecked return. Static analyzers might complain. + // If _setup_clock() succeded, we assume clock_gettime() works. + struct timespec tv_now = { 0, 0 }; + clock_gettime(GODOT_CLOCK, &tv_now); + uint64_t longtime = ((uint64_t)tv_now.tv_nsec / 1000L) + (uint64_t)tv_now.tv_sec * 1000000L; +#endif + longtime -= _clock_start; return longtime; } diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h index f4abfa2dd4..b702454603 100644 --- a/drivers/unix/os_unix.h +++ b/drivers/unix/os_unix.h @@ -42,8 +42,6 @@ class OS_Unix : public OS { - uint64_t ticks_start; - protected: // UNIX only handles the core functions. // inheriting platforms under unix (eg. X11) should handle the rest diff --git a/drivers/unix/socket_helpers.h b/drivers/unix/socket_helpers.h deleted file mode 100644 index 5b42c13eae..0000000000 --- a/drivers/unix/socket_helpers.h +++ /dev/null @@ -1,156 +0,0 @@ -/*************************************************************************/ -/* socket_helpers.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 SOCKET_HELPERS_H -#define SOCKET_HELPERS_H - -#include <string.h> - -#if defined(__MINGW32__) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 4) -// Workaround for mingw-w64 < 4.0 -#ifndef IPV6_V6ONLY -#define IPV6_V6ONLY 27 -#endif -#endif - -// helpers for sockaddr -> IP_Address and back, should work for posix and winsock. All implementations should use this - -static size_t _set_sockaddr(struct sockaddr_storage *p_addr, const IP_Address &p_ip, int p_port, IP::Type p_sock_type = IP::TYPE_ANY) { - - memset(p_addr, 0, sizeof(struct sockaddr_storage)); - - ERR_FAIL_COND_V(!p_ip.is_valid(), 0); - - // IPv6 socket - if (p_sock_type == IP::TYPE_IPV6 || p_sock_type == IP::TYPE_ANY) { - - // IPv6 only socket with IPv4 address - ERR_FAIL_COND_V(p_sock_type == IP::TYPE_IPV6 && p_ip.is_ipv4(), 0); - - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr; - addr6->sin6_family = AF_INET6; - addr6->sin6_port = htons(p_port); - copymem(&addr6->sin6_addr.s6_addr, p_ip.get_ipv6(), 16); - return sizeof(sockaddr_in6); - - } else { // IPv4 socket - - // IPv4 socket with IPv6 address - ERR_FAIL_COND_V(!p_ip.is_ipv4(), 0); - - struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr; - addr4->sin_family = AF_INET; - addr4->sin_port = htons(p_port); // short, network byte order - copymem(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 16); - return sizeof(sockaddr_in); - }; -}; - -static size_t _set_listen_sockaddr(struct sockaddr_storage *p_addr, int p_port, IP::Type p_sock_type, const IP_Address p_bind_address) { - - memset(p_addr, 0, sizeof(struct sockaddr_storage)); - if (p_sock_type == IP::TYPE_IPV4) { - struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr; - addr4->sin_family = AF_INET; - addr4->sin_port = htons(p_port); - if (p_bind_address.is_valid()) { - copymem(&addr4->sin_addr.s_addr, p_bind_address.get_ipv4(), 4); - } else { - addr4->sin_addr.s_addr = INADDR_ANY; - } - return sizeof(sockaddr_in); - } else { - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr; - - addr6->sin6_family = AF_INET6; - addr6->sin6_port = htons(p_port); - if (p_bind_address.is_valid()) { - copymem(&addr6->sin6_addr.s6_addr, p_bind_address.get_ipv6(), 16); - } else { - addr6->sin6_addr = in6addr_any; - } - return sizeof(sockaddr_in6); - }; -}; - -static int _socket_create(IP::Type &p_type, int type, int protocol) { - - ERR_FAIL_COND_V(p_type > IP::TYPE_ANY || p_type < IP::TYPE_NONE, ERR_INVALID_PARAMETER); - - int family = p_type == IP::TYPE_IPV4 ? AF_INET : AF_INET6; - int sockfd = socket(family, type, protocol); - - if (sockfd == -1 && p_type == IP::TYPE_ANY) { - // Careful here, changing the referenced parameter so the caller knows that we are using an IPv4 socket - // in place of a dual stack one, and further calls to _set_sock_addr will work as expected. - p_type = IP::TYPE_IPV4; - family = AF_INET; - sockfd = socket(family, type, protocol); - } - - ERR_FAIL_COND_V(sockfd == -1, -1); - - if (family == AF_INET6) { - // Select IPv4 over IPv6 mapping - int opt = p_type != IP::TYPE_ANY; - if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&opt, sizeof(opt)) != 0) { - WARN_PRINT("Unable to set/unset IPv4 address mapping over IPv6"); - } - } - if (protocol == IPPROTO_UDP && p_type != IP::TYPE_IPV6) { - // Enable broadcasting for UDP sockets if it's not IPv6 only (IPv6 has no broadcast option). - int broadcast = 1; - if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (char *)&broadcast, sizeof(broadcast)) != 0) { - WARN_PRINT("Error when enabling broadcasting"); - } - } - - return sockfd; -} - -static void _set_ip_addr_port(IP_Address &r_ip, int &r_port, struct sockaddr_storage *p_addr) { - - if (p_addr->ss_family == AF_INET) { - - struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr; - r_ip.set_ipv4((uint8_t *)&(addr4->sin_addr.s_addr)); - - r_port = ntohs(addr4->sin_port); - - } else if (p_addr->ss_family == AF_INET6) { - - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr; - r_ip.set_ipv6(addr6->sin6_addr.s6_addr); - - r_port = ntohs(addr6->sin6_port); - }; -}; - -#endif diff --git a/drivers/wasapi/SCsub b/drivers/wasapi/SCsub index 233593b0f9..4c24925192 100644 --- a/drivers/wasapi/SCsub +++ b/drivers/wasapi/SCsub @@ -4,5 +4,3 @@ Import('env') # Driver source files env.add_source_files(env.drivers_sources, "*.cpp") - -Export('env') diff --git a/drivers/windows/SCsub b/drivers/windows/SCsub index ee39fd2631..28b315ae66 100644 --- a/drivers/windows/SCsub +++ b/drivers/windows/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.drivers_sources, "*.cpp") - -Export('env') diff --git a/drivers/winmidi/SCsub b/drivers/winmidi/SCsub index 233593b0f9..4c24925192 100644 --- a/drivers/winmidi/SCsub +++ b/drivers/winmidi/SCsub @@ -4,5 +4,3 @@ Import('env') # Driver source files env.add_source_files(env.drivers_sources, "*.cpp") - -Export('env') diff --git a/drivers/xaudio2/SCsub b/drivers/xaudio2/SCsub index cb780a893b..3dca95b429 100644 --- a/drivers/xaudio2/SCsub +++ b/drivers/xaudio2/SCsub @@ -5,5 +5,3 @@ Import('env') env.add_source_files(env.drivers_sources, "*.cpp") env.Append(CXXFLAGS=['-DXAUDIO2_ENABLED']) env.Append(LINKFLAGS=['xaudio2_8.lib']) - -Export('env') diff --git a/drivers/zlib/SCsub b/drivers/zlib/SCsub deleted file mode 100644 index 407deb5f6e..0000000000 --- a/drivers/zlib/SCsub +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python - -Import('env') - -# Not cloning the env, the includes need to be accessible for core/ - -# Thirdparty source files -# No check here as already done in drivers/SCsub -thirdparty_dir = "#thirdparty/zlib/" -thirdparty_sources = [ - "adler32.c", - "compress.c", - "crc32.c", - "deflate.c", - "infback.c", - "inffast.c", - "inflate.c", - "inftrees.c", - "trees.c", - "uncompr.c", - "zutil.c", -] -thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - -env.add_source_files(env.drivers_sources, thirdparty_sources) -env.Append(CPPPATH=[thirdparty_dir]) diff --git a/editor/SCsub b/editor/SCsub index 82b982eef2..82a4ecb6c0 100644 --- a/editor/SCsub +++ b/editor/SCsub @@ -1,6 +1,7 @@ #!/usr/bin/env python Import('env') + env.editor_sources = [] import os @@ -78,7 +79,9 @@ if env['tools']: env.CommandNoCache('#editor/builtin_fonts.gen.h', flist, run_in_subprocess(editor_builders.make_fonts_header)) env.add_source_files(env.editor_sources, "*.cpp") - env.add_source_files(env.editor_sources, ["#thirdparty/misc/clipper.cpp"]) + env_thirdparty = env.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.editor_sources, ["#thirdparty/misc/clipper.cpp"]) SConscript('collada/SCsub') SConscript('doc/SCsub') @@ -89,5 +92,3 @@ if env['tools']: lib = env.add_library("editor", env.editor_sources) env.Prepend(LIBS=[lib]) - - Export('env') diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 6c68648bc0..77be561477 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "animation_track_editor.h" + #include "animation_track_editor_plugins.h" #include "core/os/keyboard.h" #include "editor/animation_bezier_editor.h" @@ -772,9 +773,6 @@ void AnimationTimelineEdit::_notification(int p_what) { hsize_rect = Rect2(get_name_limit() - hsize_icon->get_width() - 2 * EDSCALE, (get_size().height - hsize_icon->get_height()) / 2, hsize_icon->get_width(), hsize_icon->get_height()); draw_texture(hsize_icon, hsize_rect.position); - float keys_from = get_value(); - float keys_to = keys_from + zoomw / scale; - { float time_min = 0; float time_max = animation->get_length(); diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 33d36e5e9c..79c22f667a 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -44,7 +44,7 @@ void GotoLineDialog::popup_find_line(TextEdit *p_edit) { line->set_text(itos(text_editor->cursor_get_line())); line->select_all(); - popup_centered(Size2(180, 80)); + popup_centered(Size2(180, 80) * EDSCALE); line->grab_focus(); } @@ -65,16 +65,20 @@ void GotoLineDialog::ok_pressed() { GotoLineDialog::GotoLineDialog() { set_title(TTR("Go to Line")); + + VBoxContainer *vbc = memnew(VBoxContainer); + vbc->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_BEGIN, 8 * EDSCALE); + vbc->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 8 * EDSCALE); + vbc->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -8 * EDSCALE); + vbc->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -8 * EDSCALE); + add_child(vbc); + Label *l = memnew(Label); l->set_text(TTR("Line Number:")); - l->set_position(Point2(5, 5)); - add_child(l); + vbc->add_child(l); line = memnew(LineEdit); - line->set_anchor(MARGIN_RIGHT, ANCHOR_END); - line->set_begin(Point2(15, 22)); - line->set_end(Point2(-15, 35)); - add_child(line); + vbc->add_child(line); register_text_enter(line); text_editor = NULL; diff --git a/editor/collada/SCsub b/editor/collada/SCsub index 04c9a827ef..2b1e889fb0 100644 --- a/editor/collada/SCsub +++ b/editor/collada/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.editor_sources, "*.cpp") - -Export('env') diff --git a/editor/doc/SCsub b/editor/doc/SCsub index 04c9a827ef..2b1e889fb0 100644 --- a/editor/doc/SCsub +++ b/editor/doc/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.editor_sources, "*.cpp") - -Export('env') diff --git a/editor/editor_export.h b/editor/editor_export.h index 420f383f95..b4ee5b89e7 100644 --- a/editor/editor_export.h +++ b/editor/editor_export.h @@ -37,10 +37,10 @@ #include "scene/main/timer.h" #include "scene/resources/texture.h" -class EditorProgress; class FileAccess; class EditorExportPlatform; class EditorFileSystemDirectory; +struct EditorProgress; class EditorExportPreset : public Reference { diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp index 08fd8a1319..38bdba31ea 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -209,6 +209,10 @@ void EditorFileDialog::update_dir() { case MODE_OPEN_DIR: get_ok()->set_text(TTR("Select Current Folder")); break; + case MODE_OPEN_ANY: + case MODE_SAVE_FILE: + // FIXME: Implement, or refactor to avoid duplication with set_mode + break; } } @@ -504,6 +508,11 @@ void EditorFileDialog::_items_clear_selection() { get_ok()->set_disabled(false); get_ok()->set_text(TTR("Select Current Folder")); break; + + case MODE_OPEN_ANY: + case MODE_SAVE_FILE: + // FIXME: Implement, or refactor to avoid duplication with set_mode + break; } } diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp index ea99c882e4..8b1818b595 100644 --- a/editor/editor_fonts.cpp +++ b/editor/editor_fonts.cpp @@ -37,33 +37,6 @@ #include "scene/resources/default_theme/default_theme.h" #include "scene/resources/dynamic_font.h" -static Ref<BitmapFont> make_font(int p_height, int p_ascent, int p_valign, int p_charcount, const int *p_chars, const Ref<Texture> &p_texture) { - - Ref<BitmapFont> font(memnew(BitmapFont)); - font->add_texture(p_texture); - - for (int i = 0; i < p_charcount; i++) { - - const int *c = &p_chars[i * 8]; - - int chr = c[0]; - Rect2 frect; - frect.position.x = c[1]; - frect.position.y = c[2]; - frect.size.x = c[3]; - frect.size.y = c[4]; - Point2 align(c[5], c[6] + p_valign); - int advance = c[7]; - - font->add_char(chr, 0, frect, align, advance); - } - - font->set_height(p_height); - font->set_ascent(p_ascent); - - return font; -} - #define MAKE_FALLBACKS(m_name) \ m_name->add_fallback(FontArabic); \ m_name->add_fallback(FontHebrew); \ diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 36c3102840..2c4168f1a0 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -36,9 +36,6 @@ #include "multi_node_edit.h" #include "scene/resources/packed_scene.h" -// TODO: -// arrays and dictionary - Size2 EditorProperty::get_minimum_size() const { Size2 ms; @@ -267,11 +264,6 @@ void EditorProperty::_notification(int p_what) { } else { keying_rect = Rect2(); } - - //int vs = get_constant("vseparation", "Tree"); - Color guide_color = get_color("guide_color", "Tree"); - int vs_height = get_size().height; // vs / 2; - // draw_line(Point2(0, vs_height), Point2(get_size().width, vs_height), guide_color); } } @@ -1454,6 +1446,9 @@ void EditorInspector::update_tree() { } else if (!(p.usage & PROPERTY_USAGE_EDITOR)) continue; + if (p.usage & PROPERTY_USAGE_HIGH_END_GFX && VS::get_singleton()->is_low_end()) + continue; //do not show this property in low end gfx + if (p.name == "script" && (hide_script || bool(object->call("_hide_script_from_inspector")))) { continue; } @@ -1986,7 +1981,7 @@ void EditorInspector::_property_keyed(const String &p_path) { if (!object) return; - emit_signal("property_keyed", p_path, object->get(p_path), false); //second param is deprecated + emit_signal("property_keyed", p_path, object->get(p_path), true); //second param is deprecated } void EditorInspector::_property_keyed_with_value(const String &p_path, const Variant &p_value) { diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index 3fc35810df..5f5c46f4a7 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -49,11 +49,6 @@ void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_f err_str = String(p_file) + ":" + itos(p_line) + " - " + String(p_error); } - /* - if (!self->is_visible_in_tree()) - self->emit_signal("show_request"); - */ - if (p_type == ERR_HANDLER_WARNING) { self->add_message(err_str, MSG_TYPE_WARNING); } else { @@ -76,17 +71,6 @@ void EditorLog::_notification(int p_what) { } } } - - /*if (p_what==NOTIFICATION_DRAW) { - - RID ci = get_canvas_item(); - get_stylebox("panel","PopupMenu")->draw(ci,Rect2(Point2(),get_size())); - int top_ofs = 20; - int border_ofs=4; - Ref<StyleBox> style = get_stylebox("normal","TextEdit"); - - style->draw(ci,Rect2( Point2(border_ofs,top_ofs),get_size()-Size2(border_ofs*2,top_ofs+border_ofs))); - }*/ } void EditorLog::_clear_request() { @@ -105,6 +89,8 @@ void EditorLog::add_message(const String &p_msg, MessageType p_type) { bool restore = p_type != MSG_TYPE_STD; switch (p_type) { + case MSG_TYPE_STD: { + } break; case MSG_TYPE_ERROR: { log->push_color(get_color("error_color", "Editor")); Ref<Texture> icon = get_icon("Error", "EditorIcons"); @@ -122,7 +108,6 @@ void EditorLog::add_message(const String &p_msg, MessageType p_type) { } log->add_text(p_msg); - //button->set_text(p_msg); if (restore) log->pop(); @@ -132,21 +117,6 @@ void EditorLog::set_tool_button(ToolButton *p_tool_button) { tool_button = p_tool_button; } -/* -void EditorLog::_dragged(const Point2& p_ofs) { - - int ofs = ec->get_minsize().height; - ofs = ofs-p_ofs.y; - if (ofs<50) - ofs=50; - if (ofs>300) - ofs=300; - ec->set_minsize(Size2(ec->get_minsize().width,ofs)); - minimum_size_changed(); - -} -*/ - void EditorLog::_undo_redo_cbk(void *p_self, const String &p_name) { EditorLog *self = (EditorLog *)p_self; @@ -156,7 +126,6 @@ void EditorLog::_undo_redo_cbk(void *p_self, const String &p_name) { void EditorLog::_bind_methods() { ClassDB::bind_method(D_METHOD("_clear_request"), &EditorLog::_clear_request); - //ClassDB::bind_method(D_METHOD("_dragged"),&EditorLog::_dragged ); ADD_SIGNAL(MethodInfo("clear_request")); } @@ -187,7 +156,6 @@ EditorLog::EditorLog() { log->set_h_size_flags(SIZE_EXPAND_FILL); vb->add_child(log); add_message(VERSION_FULL_NAME " (c) 2007-2018 Juan Linietsky, Ariel Manzur & Godot Contributors."); - //log->add_text("Initialization Complete.\n"); //because it looks cool. eh.errfunc = _error_handler; eh.userdata = this; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 03746fb8b7..ea063fa798 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -36,7 +36,6 @@ #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "core/io/stream_peer_ssl.h" -#include "core/io/zip_io.h" #include "core/message_queue.h" #include "core/os/file_access.h" #include "core/os/input.h" @@ -3202,7 +3201,7 @@ Ref<Texture> EditorNode::get_class_icon(const String &p_class, const String &p_f } } - if (p_fallback.length()) + if (p_fallback.length() && gui_base->has_icon(p_fallback, "EditorIcons")) return gui_base->get_icon(p_fallback, "EditorIcons"); return NULL; @@ -4523,6 +4522,16 @@ void EditorNode::_bottom_panel_raise_toggled(bool p_pressed) { } } +void EditorNode::_update_video_driver_color() { + + //todo probably should de-harcode this and add to editor settings + if (video_driver->get_text() == "GLES2") { + video_driver->add_color_override("font_color", Color::hex(0x5586a4ff)); + } else if (video_driver->get_text() == "GLES3") { + video_driver->add_color_override("font_color", Color::hex(0xa5557dff)); + } +} + void EditorNode::_video_driver_selected(int p_which) { String driver = video_driver->get_item_metadata(p_which); @@ -4536,6 +4545,7 @@ void EditorNode::_video_driver_selected(int p_which) { video_driver_request = driver; video_restart_dialog->popup_centered_minsize(); video_driver->select(video_driver_current); + _update_video_driver_color(); } void EditorNode::_bind_methods() { @@ -4839,7 +4849,7 @@ EditorNode::EditorNode() { EDITOR_DEF_RST("interface/inspector/capitalize_properties", true); EDITOR_DEF_RST("interface/inspector/disable_folding", false); EDITOR_DEF("interface/inspector/horizontal_vector2_editing", false); - EDITOR_DEF("interface/inspector/horizontal_vector3_editing", true); + EDITOR_DEF("interface/inspector/horizontal_vector_types_editing", true); EDITOR_DEF("interface/inspector/open_resources_in_current_inspector", true); EDITOR_DEF("interface/inspector/resources_types_to_open_in_new_inspector", "SpatialMaterial,Script"); EDITOR_DEF("run/auto_save/save_before_running", true); @@ -5398,6 +5408,7 @@ EditorNode::EditorNode() { video_driver->set_focus_mode(Control::FOCUS_NONE); video_driver->set_v_size_flags(Control::SIZE_SHRINK_CENTER); video_driver->connect("item_selected", this, "_video_driver_selected"); + video_driver->add_font_override("font", gui_base->get_font("bold", "EditorFonts")); menu_hb->add_child(video_driver); String video_drivers = ProjectSettings::get_singleton()->get_custom_property_info()["rendering/quality/driver/driver_name"].hint_string; @@ -5414,6 +5425,8 @@ EditorNode::EditorNode() { } } + _update_video_driver_color(); + video_restart_dialog = memnew(ConfirmationDialog); video_restart_dialog->set_text(TTR("Changing the video driver requires restarting the editor.")); video_restart_dialog->get_ok()->set_text(TTR("Save & Restart")); diff --git a/editor/editor_node.h b/editor/editor_node.h index bdbe0a245b..0096748ed1 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -208,6 +208,7 @@ private: int video_driver_current; String video_driver_request; void _video_driver_selected(int); + void _update_video_driver_color(); // Split containers diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 1ad23963a9..dd3a8aa307 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -39,6 +39,7 @@ #include "scene/3d/camera.h" #include "scene/gui/popup_menu.h" #include "servers/visual_server.h" + Array EditorInterface::_make_mesh_previews(const Array &p_meshes, int p_preview_size) { Vector<Ref<Mesh> > meshes; @@ -522,7 +523,7 @@ int EditorPlugin::update_overlays() const { if (SpatialEditor::get_singleton()->is_visible()) { int count = 0; - for (int i = 0; i < SpatialEditor::VIEWPORTS_COUNT; i++) { + for (uint32_t i = 0; i < SpatialEditor::VIEWPORTS_COUNT; i++) { SpatialEditorViewport *vp = SpatialEditor::get_singleton()->get_editor_viewport(i); if (vp->is_visible()) { vp->update_surface(); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 3439133809..c5c78b2590 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -108,6 +108,7 @@ void EditorPropertyMultilineText::_open_big_text() { if (!big_text_dialog) { big_text = memnew(TextEdit); big_text->connect("text_changed", this, "_big_text_changed"); + big_text->set_wrap_enabled(true); big_text_dialog = memnew(AcceptDialog); big_text_dialog->add_child(big_text); big_text_dialog->set_title("Edit Text:"); @@ -152,6 +153,7 @@ EditorPropertyMultilineText::EditorPropertyMultilineText() { set_bottom_editor(hb); text = memnew(TextEdit); text->connect("text_changed", this, "_text_changed"); + text->set_wrap_enabled(true); add_focusable(text); hb->add_child(text); text->set_h_size_flags(SIZE_EXPAND_FILL); @@ -817,10 +819,10 @@ void EditorPropertyInteger::_bind_methods() { ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyInteger::_value_changed); } -void EditorPropertyInteger::setup(int p_min, int p_max, bool p_allow_greater, bool p_allow_lesser) { +void EditorPropertyInteger::setup(int p_min, int p_max, int p_step, bool p_allow_greater, bool p_allow_lesser) { spin->set_min(p_min); spin->set_max(p_max); - spin->set_step(1); + spin->set_step(p_step); spin->set_allow_greater(p_allow_greater); spin->set_allow_lesser(p_allow_lesser); } @@ -1183,21 +1185,39 @@ void EditorPropertyRect2::setup(double p_min, double p_max, double p_step, bool } EditorPropertyRect2::EditorPropertyRect2() { - VBoxContainer *vb = memnew(VBoxContainer); - add_child(vb); + + bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing"); + + BoxContainer *bc; + + if (horizontal) { + bc = memnew(HBoxContainer); + add_child(bc); + set_bottom_editor(bc); + } else { + bc = memnew(VBoxContainer); + add_child(bc); + } + static const char *desc[4] = { "x", "y", "w", "h" }; for (int i = 0; i < 4; i++) { spin[i] = memnew(EditorSpinSlider); spin[i]->set_label(desc[i]); spin[i]->set_flat(true); - - vb->add_child(spin[i]); + bc->add_child(spin[i]); add_focusable(spin[i]); spin[i]->connect("value_changed", this, "_value_changed"); + if (horizontal) { + spin[i]->set_h_size_flags(SIZE_EXPAND_FILL); + } + } + + if (!horizontal) { + set_label_reference(spin[0]); //show text and buttons around this } - set_label_reference(spin[0]); //show text and buttons around this setting = false; } + ///////////////////// VECTOR3 ///////////////////////// void EditorPropertyVector3::_value_changed(double val) { @@ -1245,7 +1265,7 @@ void EditorPropertyVector3::setup(double p_min, double p_max, double p_step, boo } EditorPropertyVector3::EditorPropertyVector3() { - bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector3_editing"); + bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing"); BoxContainer *bc; @@ -1326,7 +1346,7 @@ void EditorPropertyPlane::setup(double p_min, double p_max, double p_step, bool EditorPropertyPlane::EditorPropertyPlane() { - bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector3_editing"); + bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing"); BoxContainer *bc; @@ -1407,7 +1427,7 @@ void EditorPropertyQuat::setup(double p_min, double p_max, double p_step, bool p } EditorPropertyQuat::EditorPropertyQuat() { - bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector3_editing"); + bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing"); BoxContainer *bc; @@ -1759,7 +1779,7 @@ void EditorPropertyColor::_color_changed(const Color &p_color) { void EditorPropertyColor::_popup_closed() { - emit_signal("property_changed", get_edited_property(), picker->get_pick_color(), false); + emit_signal("property_changed", get_edited_property(), picker->get_pick_color(), true); } void EditorPropertyColor::_bind_methods() { @@ -2636,7 +2656,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } else if (p_hint == PROPERTY_HINT_LAYERS_2D_PHYSICS || p_hint == PROPERTY_HINT_LAYERS_2D_RENDER || p_hint == PROPERTY_HINT_LAYERS_3D_PHYSICS || p_hint == PROPERTY_HINT_LAYERS_3D_RENDER) { - EditorPropertyLayers::LayerType lt; + EditorPropertyLayers::LayerType lt = EditorPropertyLayers::LAYER_RENDER_2D; switch (p_hint) { case PROPERTY_HINT_LAYERS_2D_RENDER: lt = EditorPropertyLayers::LAYER_RENDER_2D; @@ -2663,7 +2683,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } else { EditorPropertyInteger *editor = memnew(EditorPropertyInteger); - int min = 0, max = 65535; + int min = 0, max = 65535, step = 1; bool greater = true, lesser = true; if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) { @@ -2671,6 +2691,11 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ lesser = false; min = p_hint_text.get_slice(",", 0).to_int(); max = p_hint_text.get_slice(",", 1).to_int(); + + if (p_hint_text.get_slice_count(",") >= 3) { + step = p_hint_text.get_slice(",", 2).to_int(); + } + for (int i = 2; i < p_hint_text.get_slice_count(","); i++) { String slice = p_hint_text.get_slice(",", i).strip_edges(); if (slice == "or_greater") { @@ -2682,7 +2707,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } } - editor->setup(min, max, greater, lesser); + editor->setup(min, max, step, greater, lesser); add_property_editor(p_path, editor); } diff --git a/editor/editor_properties.h b/editor/editor_properties.h index cfc433b880..18e70345aa 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -265,7 +265,7 @@ protected: public: virtual void update_property(); - void setup(int p_min, int p_max, bool p_allow_greater, bool p_allow_lesser); + void setup(int p_min, int p_max, int p_step, bool p_allow_greater, bool p_allow_lesser); EditorPropertyInteger(); }; diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index 808a8ac2f8..24360813a2 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -337,7 +337,7 @@ void EditorPropertyArray::update_property() { } break; case Variant::INT: { EditorPropertyInteger *editor = memnew(EditorPropertyInteger); - editor->setup(-100000, 100000, true, true); + editor->setup(-100000, 100000, 1, true, true); prop = editor; } break; @@ -744,7 +744,7 @@ void EditorPropertyDictionary::update_property() { page->connect("value_changed", this, "_page_changed"); } else { // Queue childs for deletion, delete immediately might cause errors. - for (size_t i = 1; i < vbox->get_child_count(); i++) { + for (int i = 1; i < vbox->get_child_count(); i++) { vbox->get_child(i)->queue_delete(); } } @@ -800,7 +800,7 @@ void EditorPropertyDictionary::update_property() { } break; case Variant::INT: { EditorPropertyInteger *editor = memnew(EditorPropertyInteger); - editor->setup(-100000, 100000, true, true); + editor->setup(-100000, 100000, 1, true, true); prop = editor; } break; @@ -969,7 +969,7 @@ void EditorPropertyDictionary::update_property() { pc->add_child(add_vbox); } prop->set_object_and_property(object.ptr(), prop_name); - int change_index; + int change_index = 0; if (i < amount) { String cs = key.get_construct_string(); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 6dfd5ef573..9e81051dc2 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -36,7 +36,6 @@ #include "editor_scale.h" #include "editor_settings.h" #include "modules/svg/image_loader_svg.h" -#include "time.h" static Ref<StyleBoxTexture> make_stylebox(Ref<Texture> p_texture, float p_left, float p_top, float p_right, float p_botton, float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_botton = -1, bool p_draw_center = true) { Ref<StyleBoxTexture> style(memnew(StyleBoxTexture)); @@ -82,12 +81,6 @@ static Ref<StyleBoxLine> make_line_stylebox(Color p_color, int p_thickness = 1, return style; } -static Ref<StyleBoxFlat> change_border_color(Ref<StyleBoxFlat> p_style, Color p_color) { - Ref<StyleBoxFlat> style = p_style->duplicate(); - style->set_border_color_all(p_color); - return style; -} - Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color, float p_scale = EDSCALE, bool p_force_filter = false) { Ref<ImageTexture> icon = memnew(ImageTexture); @@ -199,8 +192,6 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = exceptions.push_back("StatusWarning"); exceptions.push_back("NodeWarning"); - clock_t begin_time = clock(); - ImageLoaderSVG::set_convert_colors(&dark_icon_color_dictionary); // generate icons @@ -235,8 +226,6 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = } ImageLoaderSVG::set_convert_colors(NULL); - - clock_t end_time = clock(); #else print_line("SVG support disabled, editor icons won't be rendered."); #endif @@ -260,8 +249,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { bool use_gn_headers = EDITOR_DEF("interface/theme/use_graph_node_headers", false); - Color script_bg_color = EDITOR_DEF("text_editor/highlighting/background_color", Color(0, 0, 0, 0)); - Color preset_accent_color; Color preset_base_color; float preset_contrast; @@ -491,8 +478,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { style_tab_selected->set_bg_color(tab_color); Ref<StyleBoxFlat> style_tab_unselected = style_tab_selected->duplicate(); - style_tab_unselected->set_draw_center(false); - style_tab_unselected->set_border_width_all(0); + style_tab_unselected->set_bg_color(dark_color_1); + style_tab_unselected->set_border_color_all(dark_color_2); // Editor background theme->set_stylebox("Background", "EditorStyles", make_flat_stylebox(background_color, default_margin_size, default_margin_size, default_margin_size, default_margin_size)); @@ -1069,8 +1056,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { const float mono_value = mono_color.r; const Color alpha1 = Color(mono_value, mono_value, mono_value, 0.07); const Color alpha2 = Color(mono_value, mono_value, mono_value, 0.14); - const Color alpha3 = Color(mono_value, mono_value, mono_value, 0.5); - const Color alpha4 = Color(mono_value, mono_value, mono_value, 0.7); + const Color alpha3 = Color(mono_value, mono_value, mono_value, 0.7); // editor main color const Color main_color = Color::html(dark_theme ? "#57b3ff" : "#0480ff"); @@ -1104,9 +1090,9 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { const Color member_variable_color = main_color.linear_interpolate(mono_color, 0.6); const Color mark_color = Color(error_color.r, error_color.g, error_color.b, 0.3); const Color breakpoint_color = error_color; - const Color code_folding_color = alpha4; + const Color code_folding_color = alpha3; const Color search_result_color = alpha1; - const Color search_result_border_color = alpha4; + const Color search_result_border_color = alpha3; EditorSettings *setting = EditorSettings::get_singleton(); String text_editor_color_theme = setting->get("text_editor/theme/color_theme"); diff --git a/editor/fileserver/SCsub b/editor/fileserver/SCsub index f1fa50148f..2b1e889fb0 100644 --- a/editor/fileserver/SCsub +++ b/editor/fileserver/SCsub @@ -1,5 +1,5 @@ #!/usr/bin/env python Import('env') -Export('env') + env.add_source_files(env.editor_sources, "*.cpp") diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 607ced4573..2c69909f23 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -73,14 +73,7 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory if ((path.begins_with(lpath) && path != lpath)) { subdirectory_item->set_collapsed(false); } else { - bool is_collapsed = true; - for (int i = 0; i < uncollapsed_paths.size(); i++) { - if (lpath == uncollapsed_paths[i]) { - is_collapsed = false; - break; - } - } - subdirectory_item->set_collapsed(is_collapsed); + subdirectory_item->set_collapsed(uncollapsed_paths.find(lpath) < 0); } if (searched_string.length() > 0 && dname.to_lower().find(searched_string) >= 0) { parent_should_expand = true; @@ -137,6 +130,11 @@ Vector<String> FileSystemDock::_compute_uncollapsed_paths() { Vector<String> uncollapsed_paths; TreeItem *root = tree->get_root(); if (root) { + TreeItem *favorites_item = root->get_children(); + if (!favorites_item->is_collapsed()) { + uncollapsed_paths.push_back(favorites_item->get_metadata(0)); + } + TreeItem *resTree = root->get_children()->get_next(); if (resTree) { Vector<TreeItem *> needs_check; @@ -170,15 +168,14 @@ void FileSystemDock::_update_tree(const Vector<String> p_uncollapsed_paths, bool TreeItem *favorites = tree->create_item(root); favorites->set_icon(0, get_icon("Favorites", "EditorIcons")); favorites->set_text(0, TTR("Favorites:")); - favorites->set_selectable(0, false); + favorites->set_metadata(0, "Favorites"); + favorites->set_collapsed(p_uncollapsed_paths.find("Favorites") < 0); Vector<String> favorite_paths = EditorSettings::get_singleton()->get_favorites(); for (int i = 0; i < favorite_paths.size(); i++) { String fave = favorite_paths[i]; if (!fave.begins_with("res://")) continue; - if (display_mode_setting == DISPLAY_MODE_SETTING_SPLIT && !fave.ends_with("/")) - continue; Ref<Texture> folder_icon = get_icon("Folder", "EditorIcons"); @@ -208,6 +205,12 @@ void FileSystemDock::_update_tree(const Vector<String> p_uncollapsed_paths, bool ti->set_tooltip(0, fave); ti->set_selectable(0, true); ti->set_metadata(0, fave); + if (!fave.ends_with("/")) { + Array udata; + udata.push_back(tree_update_id); + udata.push_back(ti); + EditorResourcePreview::get_singleton()->queue_resource_preview(fave, this, "_tree_thumbnail_done", udata); + } } } @@ -255,7 +258,7 @@ void FileSystemDock::_update_display_mode() { button_tree->show(); file_list_vb->show(); - _update_files(true); + _update_file_list(true); break; case DISPLAY_MODE_SPLIT: @@ -267,7 +270,7 @@ void FileSystemDock::_update_display_mode() { _update_tree(_compute_uncollapsed_paths()); file_list_vb->show(); - _update_files(true); + _update_file_list(true); break; } } @@ -384,7 +387,7 @@ void FileSystemDock::_notification(int p_what) { } if (should_update_files) { - _update_files(true); + _update_file_list(true); } // Change full tree mode @@ -404,18 +407,26 @@ void FileSystemDock::_tree_multi_selected(Object *p_item, int p_column, bool p_s return; // Tree item selected - TreeItem *sel = tree->get_selected(); - if (!sel) + TreeItem *selected = tree->get_selected(); + if (!selected) return; - path = sel->get_metadata(0); + + TreeItem *favorites_item = tree->get_root()->get_children(); + if (selected->get_parent() == favorites_item) { + // Go to the favorites if we click in the favorites and the path has changed + path = "Favorites"; + } else { + path = selected->get_metadata(0); + // Note: the "Favorites" item also leads to this path + } // Set the current path - current_path->set_text(path); + _set_current_path_text(path); _push_to_history(); // Update the file list if (!updating_tree && display_mode == DISPLAY_MODE_SPLIT) { - _update_files(false); + _update_file_list(false); } } @@ -431,37 +442,52 @@ String FileSystemDock::get_current_path() const { return path; } +void FileSystemDock::_set_current_path_text(const String &p_path) { + if (p_path == "Favorites") { + current_path->set_text(TTR("Favorites")); + } else { + current_path->set_text(path); + } +} + void FileSystemDock::navigate_to_path(const String &p_path) { - String target_path = p_path; - // If the path is a file, do not only go to the directory in the tree, also select the file in the file list. - if (target_path.ends_with("/")) { - target_path = target_path.substr(0, target_path.length() - 1); - } - DirAccess *dirAccess = DirAccess::open("res://"); - if (dirAccess->file_exists(p_path)) { - path = target_path; - } else if (dirAccess->dir_exists(p_path)) { - path = target_path + "/"; + if (p_path == "Favorites") { + path = p_path; } else { - ERR_EXPLAIN(vformat(TTR("Cannot navigate to '%s' as it has not been found in the file system!"), p_path)); - ERR_FAIL(); + String target_path = p_path; + // If the path is a file, do not only go to the directory in the tree, also select the file in the file list. + if (target_path.ends_with("/")) { + target_path = target_path.substr(0, target_path.length() - 1); + } + DirAccess *dirAccess = DirAccess::open("res://"); + if (dirAccess->file_exists(p_path)) { + path = target_path; + } else if (dirAccess->dir_exists(p_path)) { + path = target_path + "/"; + } else { + ERR_EXPLAIN(vformat(TTR("Cannot navigate to '%s' as it has not been found in the file system!"), p_path)); + ERR_FAIL(); + } } - current_path->set_text(path); + _set_current_path_text(path); _push_to_history(); if (display_mode == DISPLAY_MODE_SPLIT) { + if (path.ends_with("/") || path == "Favorites") { + _go_to_file_list(); + } _update_tree(_compute_uncollapsed_paths()); - _update_files(false); + _update_file_list(false); } else if (display_mode == DISPLAY_MODE_TREE_ONLY) { - if (path.ends_with("/")) { + if (path.ends_with("/") || path == "Favorites") { _go_to_file_list(); } else { _update_tree(_compute_uncollapsed_paths()); } } else { // DISPLAY_MODE_FILE_LIST_ONLY - _update_files(true); + _update_file_list(true); } String file_name = p_path.get_file(); @@ -482,8 +508,14 @@ void FileSystemDock::_file_list_thumbnail_done(const String &p_path, const Ref<T Array uarr = p_udata; int idx = uarr[0]; String file = uarr[1]; - if (idx < files->get_item_count() && files->get_item_text(idx) == file && files->get_item_metadata(idx) == p_path) - files->set_item_icon(idx, p_preview); + if (idx < files->get_item_count() && files->get_item_text(idx) == file && files->get_item_metadata(idx) == p_path) { + if (file_list_display_mode == FILE_LIST_DISPLAY_LIST) { + if (p_small_preview.is_valid()) + files->set_item_icon(idx, p_small_preview); + } else { + files->set_item_icon(idx, p_preview); + } + } } } @@ -494,15 +526,6 @@ void FileSystemDock::_tree_thumbnail_done(const String &p_path, const Ref<Textur TreeItem *file_item = Object::cast_to<TreeItem>(uarr[1]); if (file_item) { file_item->set_icon(0, p_small_preview); - - // Update the favorite icon if needed - TreeItem *favorite = tree->get_root()->get_children()->get_children(); - while (favorite) { - if (favorite->get_metadata(0) == file_item->get_metadata(0)) { - favorite->set_icon(0, p_small_preview); - } - favorite = favorite->get_next(); - } } } } @@ -527,7 +550,7 @@ void FileSystemDock::_change_file_display() { EditorSettings::get_singleton()->set("docks/filesystem/files_display_mode", file_list_display_mode); - _update_files(true); + _update_file_list(true); } void FileSystemDock::_search(EditorFileSystemDirectory *p_path, List<FileInfo> *matches, int p_max_items) { @@ -558,7 +581,7 @@ void FileSystemDock::_search(EditorFileSystemDirectory *p_path, List<FileInfo> * } } -void FileSystemDock::_update_files(bool p_keep_selection) { +void FileSystemDock::_update_file_list(bool p_keep_selection) { // Register the previously selected items Set<String> cselection; @@ -571,21 +594,10 @@ void FileSystemDock::_update_files(bool p_keep_selection) { files->clear(); - current_path->set_text(path); + _set_current_path_text(path); String directory = path; - if (directory.ends_with("/") && directory != "res://") { - directory = directory.substr(0, directory.length() - 1); - } String file = ""; - EditorFileSystemDirectory *efd = EditorFileSystem::get_singleton()->get_filesystem_path(directory); - if (!efd) { - directory = path.get_base_dir(); - file = path.get_file(); - efd = EditorFileSystem::get_singleton()->get_filesystem_path(directory); - } - if (!efd) - return; String ei = "EditorIcons"; int thumbnail_size = EditorSettings::get_singleton()->get("docks/filesystem/thumbnail_size"); @@ -595,10 +607,9 @@ void FileSystemDock::_update_files(bool p_keep_selection) { Ref<Texture> file_thumbnail_broken; bool use_thumbnails = (file_list_display_mode == FILE_LIST_DISPLAY_THUMBNAILS); - bool use_folders = searched_string.length() == 0 && ((display_mode == DISPLAY_MODE_FILE_LIST_ONLY || display_mode == DISPLAY_MODE_TREE_ONLY) || always_show_folders); if (use_thumbnails) { - + // Thumbnails mode files->set_max_columns(0); files->set_icon_mode(ItemList::ICON_MODE_TOP); files->set_fixed_column_width(thumbnail_size * 3 / 2); @@ -616,6 +627,7 @@ void FileSystemDock::_update_files(bool p_keep_selection) { } } else { + // No thumbnails files->set_icon_mode(ItemList::ICON_MODE_LEFT); files->set_max_columns(1); files->set_max_text_lines(1); @@ -623,57 +635,117 @@ void FileSystemDock::_update_files(bool p_keep_selection) { files->set_fixed_icon_size(Size2()); } - if (use_folders) { - Ref<Texture> folderIcon = (use_thumbnails) ? folder_thumbnail : get_icon("folder", "FileDialog"); + Ref<Texture> folder_icon = (use_thumbnails) ? folder_thumbnail : get_icon("folder", "FileDialog"); - if (directory != "res://") { - files->add_item("..", folderIcon, true); + // Build the FileInfo list + List<FileInfo> filelist; + if (path == "Favorites") { + // Display the favorites + Vector<String> favorites = EditorSettings::get_singleton()->get_favorites(); + for (int i = 0; i < favorites.size(); i++) { + String favorite = favorites[i]; + String text; + Ref<Texture> icon; + if (favorite == "res://") { + text = "/"; + icon = folder_icon; + if (searched_string.length() == 0 || text.to_lower().find(searched_string) >= 0) { + files->add_item(text, icon, true); + files->set_item_metadata(files->get_item_count() - 1, favorite); + } + } else if (favorite.ends_with("/")) { + text = favorite.substr(0, favorite.length() - 1).get_file(); + icon = folder_icon; + if (searched_string.length() == 0 || text.to_lower().find(searched_string) >= 0) { + files->add_item(text, icon, true); + files->set_item_metadata(files->get_item_count() - 1, favorite); + } + } else { + int index; + EditorFileSystemDirectory *efd = EditorFileSystem::get_singleton()->find_file(favorite, &index); + + FileInfo fi; + fi.name = favorite.get_file(); + fi.path = favorite; + if (efd) { + fi.type = efd->get_file_type(index); + fi.import_broken = !efd->get_file_import_is_valid(index); + } else { + fi.type = ""; + fi.import_broken = true; + } + fi.import_status = 0; - String bd = directory.get_base_dir(); - if (bd != "res://" && !bd.ends_with("/")) - bd += "/"; + if (searched_string.length() == 0 || fi.name.to_lower().find(searched_string) >= 0) { + filelist.push_back(fi); + } + } + } + } else { - files->set_item_metadata(files->get_item_count() - 1, bd); - files->set_item_selectable(files->get_item_count() - 1, false); + // Get infos on the directory + file + if (directory.ends_with("/") && directory != "res://") { + directory = directory.substr(0, directory.length() - 1); + } + EditorFileSystemDirectory *efd = EditorFileSystem::get_singleton()->get_filesystem_path(directory); + if (!efd) { + directory = path.get_base_dir(); + file = path.get_file(); + efd = EditorFileSystem::get_singleton()->get_filesystem_path(directory); } + if (!efd) + return; - for (int i = 0; i < efd->get_subdir_count(); i++) { + if (searched_string.length() > 0) { + // Display the search results + _search(EditorFileSystem::get_singleton()->get_filesystem(), &filelist, 128); + } else { - String dname = efd->get_subdir(i)->get_name(); + if ((display_mode == DISPLAY_MODE_FILE_LIST_ONLY || display_mode == DISPLAY_MODE_TREE_ONLY) || always_show_folders) { + // Display folders in the list - files->add_item(dname, folderIcon, true); - files->set_item_metadata(files->get_item_count() - 1, directory.plus_file(dname) + "/"); + if (directory != "res://") { + files->add_item("..", folder_icon, true); - if (cselection.has(dname)) { - files->select(files->get_item_count() - 1, false); - } - } - } + String bd = directory.get_base_dir(); + if (bd != "res://" && !bd.ends_with("/")) + bd += "/"; - List<FileInfo> filelist; + files->set_item_metadata(files->get_item_count() - 1, bd); + files->set_item_selectable(files->get_item_count() - 1, false); + } - if (searched_string.length() > 0) { + for (int i = 0; i < efd->get_subdir_count(); i++) { - _search(EditorFileSystem::get_singleton()->get_filesystem(), &filelist, 128); - filelist.sort(); - } else { + String dname = efd->get_subdir(i)->get_name(); - for (int i = 0; i < efd->get_file_count(); i++) { + files->add_item(dname, folder_icon, true); + files->set_item_metadata(files->get_item_count() - 1, directory.plus_file(dname) + "/"); - FileInfo fi; - fi.name = efd->get_file(i); - fi.path = directory.plus_file(fi.name); - fi.type = efd->get_file_type(i); - fi.import_broken = !efd->get_file_import_is_valid(i); - fi.import_status = 0; + if (cselection.has(dname)) { + files->select(files->get_item_count() - 1, false); + } + } + } + + // Display the folder content + for (int i = 0; i < efd->get_file_count(); i++) { + + FileInfo fi; + fi.name = efd->get_file(i); + fi.path = directory.plus_file(fi.name); + fi.type = efd->get_file_type(i); + fi.import_broken = !efd->get_file_import_is_valid(i); + fi.import_status = 0; - filelist.push_back(fi); + filelist.push_back(fi); + } } filelist.sort(); } + // Fills the ItemList control node from the FileInfos String oi = "Object"; - for (List<FileInfo>::Element *E = filelist.front(); E; E = E->next()) { FileInfo *finfo = &(E->get()); String fname = finfo->name; @@ -685,6 +757,7 @@ void FileSystemDock::_update_files(bool p_keep_selection) { String tooltip = fname; + // Select the icons if (!finfo->import_broken) { type_icon = (has_icon(ftype, ei)) ? get_icon(ftype, ei) : get_icon(oi, ei); big_icon = file_thumbnail; @@ -694,25 +767,30 @@ void FileSystemDock::_update_files(bool p_keep_selection) { tooltip += "\n" + TTR("Status: Import of file failed. Please fix file and reimport manually."); } + // Add the item to the ItemList int item_index; if (use_thumbnails) { files->add_item(fname, big_icon, true); item_index = files->get_item_count() - 1; files->set_item_metadata(item_index, fpath); files->set_item_tag_icon(item_index, type_icon); - if (!finfo->import_broken) { - Array udata; - udata.resize(2); - udata[0] = item_index; - udata[1] = fname; - EditorResourcePreview::get_singleton()->queue_resource_preview(fpath, this, "_file_list_thumbnail_done", udata); - } + } else { files->add_item(fname, type_icon, true); item_index = files->get_item_count() - 1; files->set_item_metadata(item_index, fpath); } + // Generate the preview + if (!finfo->import_broken) { + Array udata; + udata.resize(2); + udata[0] = item_index; + udata[1] = fname; + EditorResourcePreview::get_singleton()->queue_resource_preview(fpath, this, "_file_list_thumbnail_done", udata); + } + + // Select the items if (cselection.has(fname)) files->select(item_index, false); @@ -721,6 +799,7 @@ void FileSystemDock::_update_files(bool p_keep_selection) { files->ensure_current_is_visible(); } + // Tooltip if (finfo->sources.size()) { for (int j = 0; j < finfo->sources.size(); j++) { tooltip += "\nSource: " + finfo->sources[j]; @@ -736,14 +815,14 @@ void FileSystemDock::_select_file(const String p_path) { if (fpath != "res://") { fpath = fpath.substr(0, fpath.length() - 1); } - navigate_to_path(fpath); - } else { + } else if (fpath != "Favorites") { if (ResourceLoader::get_resource_type(fpath) == "PackedScene") { editor->open_request(fpath); } else { editor->load_resource(fpath); } } + navigate_to_path(fpath); } void FileSystemDock::_tree_activate_file() { @@ -764,9 +843,12 @@ void FileSystemDock::_go_to_file_list() { file_list_view = true; _update_display_mode(); } else { - bool collapsed = tree->get_selected()->is_collapsed(); - tree->get_selected()->set_collapsed(!collapsed); - _update_files(false); + TreeItem *selected = tree->get_selected(); + if (selected) { + bool collapsed = selected->is_collapsed(); + selected->set_collapsed(!collapsed); + } + _update_file_list(false); } } @@ -809,7 +891,7 @@ void FileSystemDock::_fs_changed() { } if (file_list_vb->is_visible()) { - _update_files(true); + _update_file_list(true); } set_process(false); @@ -846,7 +928,7 @@ void FileSystemDock::_bw_history() { void FileSystemDock::_update_history() { path = history[history_pos]; - current_path->set_text(path); + _set_current_path_text(path); if (tree->is_visible()) { _update_tree(_compute_uncollapsed_paths()); @@ -855,7 +937,7 @@ void FileSystemDock::_update_history() { } if (file_list_vb->is_visible()) { - _update_files(false); + _update_file_list(false); } button_hist_prev->set_disabled(history_pos == 0); @@ -1284,15 +1366,16 @@ Vector<String> FileSystemDock::_tree_get_selected(bool remove_self_inclusion) { // Build a list of selected items with the active one at the first position Vector<String> selected_strings; + TreeItem *favorites_item = tree->get_root()->get_children(); TreeItem *active_selected = tree->get_selected(); - if (active_selected) { + if (active_selected && active_selected != favorites_item) { selected_strings.push_back(active_selected->get_metadata(0)); } TreeItem *selected = tree->get_root(); selected = tree->get_next_selected(selected); while (selected) { - if (selected != active_selected) { + if (selected != active_selected && selected != favorites_item) { selected_strings.push_back(selected->get_metadata(0)); } selected = tree->get_next_selected(selected); @@ -1413,6 +1496,8 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> p_selected) } EditorSettings::get_singleton()->set_favorites(favorites); _update_tree(_compute_uncollapsed_paths()); + if (path == "Favorites") + _update_file_list(true); } break; case FILE_DEPENDENCIES: { @@ -1592,13 +1677,13 @@ void FileSystemDock::_search_changed(const String &p_text, const Control *p_from switch (display_mode) { case DISPLAY_MODE_FILE_LIST_ONLY: { - _update_files(false); + _update_file_list(false); } break; case DISPLAY_MODE_TREE_ONLY: { _update_tree(searched_string.length() == 0 ? uncollapsed_paths_before_search : Vector<String>()); } break; case DISPLAY_MODE_SPLIT: { - _update_files(false); + _update_file_list(false); _update_tree(searched_string.length() == 0 ? uncollapsed_paths_before_search : Vector<String>()); } break; } @@ -1650,6 +1735,12 @@ Variant FileSystemDock::get_drag_data_fw(const Point2 &p_point, Control *p_from) // Check if the first selected is in favorite TreeItem *selected = tree->get_next_selected(tree->get_root()); while (selected) { + TreeItem *favorites_item = tree->get_root()->get_children(); + if (selected == favorites_item) { + // The "Favorites" item is not draggable + return Variant(); + } + bool is_favorite = selected->get_parent() != NULL && tree->get_root()->get_children() == selected->get_parent(); all_favorites &= is_favorite; all_not_favorites &= !is_favorite; @@ -1805,6 +1896,9 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data, EditorSettings::get_singleton()->set_favorites(dirs); _update_tree(_compute_uncollapsed_paths()); + + if (display_mode == DISPLAY_MODE_SPLIT && path == "Favorites") + _update_file_list(true); return; } @@ -1859,7 +1953,7 @@ void FileSystemDock::_get_drag_target_folder(String &target, bool &target_favori } String ltarget = files->get_item_metadata(pos); - target = ltarget.ends_with("/") ? target : path; + target = ltarget.ends_with("/") ? ltarget : path; return; } @@ -2268,7 +2362,7 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { current_path = memnew(LineEdit); current_path->set_h_size_flags(SIZE_EXPAND_FILL); - current_path->set_text(path); + _set_current_path_text(path); toolbar_hbc->add_child(current_path); button_reload = memnew(Button); diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 51c8791b25..d964515572 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -188,7 +188,7 @@ private: void _file_list_gui_input(Ref<InputEvent> p_event); void _tree_gui_input(Ref<InputEvent> p_event); - void _update_files(bool p_keep_selection); + void _update_file_list(bool p_keep_selection); void _update_file_list_display_mode_button(); void _change_file_display(); void _fs_changed(); @@ -257,6 +257,8 @@ private: void _search(EditorFileSystemDirectory *p_path, List<FileInfo> *matches, int p_max_items); + void _set_current_path_text(const String &p_path); + Variant get_drag_data_fw(const Point2 &p_point, Control *p_from); bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index 2fdf1c82c0..2af81b8ac7 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -406,11 +406,8 @@ FindInFilesDialog::FindInFilesDialog() { HBoxContainer *hbc = memnew(HBoxContainer); hbc->set_alignment(HBoxContainer::ALIGN_CENTER); - _find_button = memnew(Button); - _find_button->set_text(TTR("Find...")); - _find_button->connect("pressed", this, "_on_find_button_pressed"); + _find_button = add_button(TTR("Find..."), false, "find"); _find_button->set_disabled(true); - hbc->add_child(_find_button); { Control *placeholder = memnew(Control); @@ -418,11 +415,8 @@ FindInFilesDialog::FindInFilesDialog() { hbc->add_child(placeholder); } - _replace_button = memnew(Button); - _replace_button->set_text(TTR("Replace...")); - _replace_button->connect("pressed", this, "_on_replace_button_pressed"); + _replace_button = add_button(TTR("Replace..."), false, "replace"); _replace_button->set_disabled(true); - hbc->add_child(_replace_button); { Control *placeholder = memnew(Control); @@ -430,10 +424,8 @@ FindInFilesDialog::FindInFilesDialog() { hbc->add_child(placeholder); } - Button *cancel_button = memnew(Button); + Button *cancel_button = get_ok(); cancel_button->set_text(TTR("Cancel")); - cancel_button->connect("pressed", this, "hide"); - hbc->add_child(cancel_button); vbc->add_child(hbc); } @@ -487,14 +479,14 @@ void FindInFilesDialog::_on_folder_button_pressed() { _folder_dialog->popup_centered_ratio(); } -void FindInFilesDialog::_on_find_button_pressed() { - emit_signal(SIGNAL_FIND_REQUESTED); - hide(); -} - -void FindInFilesDialog::_on_replace_button_pressed() { - emit_signal(SIGNAL_REPLACE_REQUESTED); - hide(); +void FindInFilesDialog::custom_action(const String &p_action) { + if (p_action == "find") { + emit_signal(SIGNAL_FIND_REQUESTED); + hide(); + } else if (p_action == "replace") { + emit_signal(SIGNAL_REPLACE_REQUESTED); + hide(); + } } void FindInFilesDialog::_on_search_text_modified(String text) { @@ -509,7 +501,7 @@ void FindInFilesDialog::_on_search_text_modified(String text) { void FindInFilesDialog::_on_search_text_entered(String text) { // This allows to trigger a global search without leaving the keyboard if (!_find_button->is_disabled()) - _on_find_button_pressed(); + custom_action("find"); } void FindInFilesDialog::_on_folder_selected(String path) { @@ -522,8 +514,6 @@ void FindInFilesDialog::_on_folder_selected(String path) { void FindInFilesDialog::_bind_methods() { ClassDB::bind_method("_on_folder_button_pressed", &FindInFilesDialog::_on_folder_button_pressed); - ClassDB::bind_method("_on_find_button_pressed", &FindInFilesDialog::_on_find_button_pressed); - ClassDB::bind_method("_on_replace_button_pressed", &FindInFilesDialog::_on_replace_button_pressed); ClassDB::bind_method("_on_folder_selected", &FindInFilesDialog::_on_folder_selected); ClassDB::bind_method("_on_search_text_modified", &FindInFilesDialog::_on_search_text_modified); ClassDB::bind_method("_on_search_text_entered", &FindInFilesDialog::_on_search_text_entered); diff --git a/editor/find_in_files.h b/editor/find_in_files.h index 75ea1c3161..7f37123430 100644 --- a/editor/find_in_files.h +++ b/editor/find_in_files.h @@ -91,8 +91,8 @@ class CheckBox; class FileDialog; // Prompts search parameters -class FindInFilesDialog : public WindowDialog { - GDCLASS(FindInFilesDialog, WindowDialog) +class FindInFilesDialog : public AcceptDialog { + GDCLASS(FindInFilesDialog, AcceptDialog) public: static const char *SIGNAL_FIND_REQUESTED; static const char *SIGNAL_REPLACE_REQUESTED; @@ -111,11 +111,10 @@ protected: static void _bind_methods(); void _notification(int p_what); + void custom_action(const String &p_action); private: void _on_folder_button_pressed(); - void _on_find_button_pressed(); - void _on_replace_button_pressed(); void _on_folder_selected(String path); void _on_search_text_modified(String text); void _on_search_text_entered(String text); diff --git a/editor/icons/SCsub b/editor/icons/SCsub index 31bf8f116a..109e1aa83b 100644 --- a/editor/icons/SCsub +++ b/editor/icons/SCsub @@ -1,15 +1,13 @@ #!/usr/bin/env python Import('env') + from platform_methods import run_in_subprocess import editor_icons_builders - make_editor_icons_builder = Builder(action=run_in_subprocess(editor_icons_builders.make_editor_icons_action), suffix='.h', src_suffix='.svg') env['BUILDERS']['MakeEditorIconsBuilder'] = make_editor_icons_builder env.Alias('editor_icons', [env.MakeEditorIconsBuilder('#editor/editor_icons.gen.h', Glob("*.svg"))]) - -Export('env') diff --git a/editor/import/SCsub b/editor/import/SCsub index f1fa50148f..2b1e889fb0 100644 --- a/editor/import/SCsub +++ b/editor/import/SCsub @@ -1,5 +1,5 @@ #!/usr/bin/env python Import('env') -Export('env') + env.add_source_files(env.editor_sources, "*.cpp") diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp index 31c1886d32..60ca66e464 100644 --- a/editor/import/editor_import_collada.cpp +++ b/editor/import/editor_import_collada.cpp @@ -490,120 +490,6 @@ Error ColladaImport::_create_material(const String &p_target) { return OK; } -static void _generate_normals(const PoolVector<int> &p_indices, const PoolVector<Vector3> &p_vertices, PoolVector<Vector3> &r_normals) { - - r_normals.resize(p_vertices.size()); - PoolVector<Vector3>::Write narrayw = r_normals.write(); - - int iacount = p_indices.size() / 3; - PoolVector<int>::Read index_arrayr = p_indices.read(); - PoolVector<Vector3>::Read vertex_arrayr = p_vertices.read(); - - for (int idx = 0; idx < iacount; idx++) { - - Vector3 v[3] = { - vertex_arrayr[index_arrayr[idx * 3 + 0]], - vertex_arrayr[index_arrayr[idx * 3 + 1]], - vertex_arrayr[index_arrayr[idx * 3 + 2]] - }; - - Vector3 normal = Plane(v[0], v[1], v[2]).normal; - - narrayw[index_arrayr[idx * 3 + 0]] += normal; - narrayw[index_arrayr[idx * 3 + 1]] += normal; - narrayw[index_arrayr[idx * 3 + 2]] += normal; - } - - int vlen = p_vertices.size(); - - for (int idx = 0; idx < vlen; idx++) { - narrayw[idx].normalize(); - } -} - -static void _generate_tangents_and_binormals(const PoolVector<int> &p_indices, const PoolVector<Vector3> &p_vertices, const PoolVector<Vector3> &p_uvs, const PoolVector<Vector3> &p_normals, PoolVector<real_t> &r_tangents) { - - int vlen = p_vertices.size(); - - Vector<Vector3> tangents; - tangents.resize(vlen); - Vector<Vector3> binormals; - binormals.resize(vlen); - - int iacount = p_indices.size() / 3; - - PoolVector<int>::Read index_arrayr = p_indices.read(); - PoolVector<Vector3>::Read vertex_arrayr = p_vertices.read(); - PoolVector<Vector3>::Read narrayr = p_normals.read(); - PoolVector<Vector3>::Read uvarrayr = p_uvs.read(); - - for (int idx = 0; idx < iacount; idx++) { - - Vector3 v1 = vertex_arrayr[index_arrayr[idx * 3 + 0]]; - Vector3 v2 = vertex_arrayr[index_arrayr[idx * 3 + 1]]; - Vector3 v3 = vertex_arrayr[index_arrayr[idx * 3 + 2]]; - - Vector3 w1 = uvarrayr[index_arrayr[idx * 3 + 0]]; - Vector3 w2 = uvarrayr[index_arrayr[idx * 3 + 1]]; - Vector3 w3 = uvarrayr[index_arrayr[idx * 3 + 2]]; - - real_t x1 = v2.x - v1.x; - real_t x2 = v3.x - v1.x; - real_t y1 = v2.y - v1.y; - real_t y2 = v3.y - v1.y; - real_t z1 = v2.z - v1.z; - real_t z2 = v3.z - v1.z; - - real_t s1 = w2.x - w1.x; - real_t s2 = w3.x - w1.x; - real_t t1 = w2.y - w1.y; - real_t t2 = w3.y - w1.y; - - real_t r = (s1 * t2 - s2 * t1); - - Vector3 tangent; - Vector3 binormal; - - if (r == 0) { - - binormal = Vector3(); - tangent = Vector3(); - } else { - tangent = Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, - (t2 * z1 - t1 * z2) * r) - .normalized(); - binormal = Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, - (s1 * z2 - s2 * z1) * r) - .normalized(); - } - - tangents.write[index_arrayr[idx * 3 + 0]] += tangent; - binormals.write[index_arrayr[idx * 3 + 0]] += binormal; - tangents.write[index_arrayr[idx * 3 + 1]] += tangent; - binormals.write[index_arrayr[idx * 3 + 1]] += binormal; - tangents.write[index_arrayr[idx * 3 + 2]] += tangent; - binormals.write[index_arrayr[idx * 3 + 2]] += binormal; - } - - r_tangents.resize(vlen * 4); - PoolVector<real_t>::Write tarrayw = r_tangents.write(); - - for (int idx = 0; idx < vlen; idx++) { - Vector3 tangent = tangents[idx]; - Vector3 bingen = narrayr[idx].cross(tangent); - float dir; - if (bingen.dot(binormals[idx]) < 0) - dir = -1.0; - else - dir = +1.0; - - tarrayw[idx * 4 + 0] = tangent.x; - tarrayw[idx * 4 + 1] = tangent.y; - tarrayw[idx * 4 + 2] = tangent.z; - tarrayw[idx * 4 + 3] = dir; - } -} - Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_mesh, const Map<String, Collada::NodeGeometry::Material> &p_material_map, const Collada::MeshData &meshdata, const Transform &p_local_xform, const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_controller, const Collada::MorphControllerData *p_morph_data, Vector<Ref<ArrayMesh> > p_morph_meshes, bool p_use_compression, bool p_use_mesh_material) { bool local_xform_mirror = p_local_xform.basis.determinant() < 0; diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp index 85383fd69d..a6b754de3b 100644 --- a/editor/import/editor_scene_importer_gltf.cpp +++ b/editor/import/editor_scene_importer_gltf.cpp @@ -1910,15 +1910,15 @@ void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlaye NodePath node_path; GLTFNode *node = state.nodes[E->key()]; - for (int i = 0; i < node->godot_nodes.size(); i++) { + for (int n = 0; n < node->godot_nodes.size(); n++) { if (node->joints.size()) { - Skeleton *sk = (Skeleton *)node->godot_nodes[i]; + Skeleton *sk = (Skeleton *)node->godot_nodes[n]; String path = ap->get_parent()->get_path_to(sk); - String bone = sk->get_bone_name(node->joints[i].godot_bone_index); + String bone = sk->get_bone_name(node->joints[n].godot_bone_index); node_path = path + ":" + bone; } else { - node_path = ap->get_parent()->get_path_to(node->godot_nodes[i]); + node_path = ap->get_parent()->get_path_to(node->godot_nodes[n]); } for (int i = 0; i < track.rotation_track.times.size(); i++) { @@ -1993,8 +1993,8 @@ void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlaye xform.basis.set_quat_scale(rot, scale); xform.origin = pos; - Skeleton *skeleton = skeletons[node->joints[i].skin]; - int bone = node->joints[i].godot_bone_index; + Skeleton *skeleton = skeletons[node->joints[n].skin]; + int bone = node->joints[n].godot_bone_index; xform = skeleton->get_bone_rest(bone).affine_inverse() * xform; rot = xform.basis.get_rotation_quat(); diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index 81a798f0b6..4f4980d83c 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -259,6 +259,8 @@ void InspectorDock::_prepare_history() { } history_menu->get_popup()->add_icon_item(icon, text, i); } + + editor_path->update_path(); } void InspectorDock::_select_history(int p_idx) const { diff --git a/editor/plugins/SCsub b/editor/plugins/SCsub index f1fa50148f..2b1e889fb0 100644 --- a/editor/plugins/SCsub +++ b/editor/plugins/SCsub @@ -1,5 +1,5 @@ #!/usr/bin/env python Import('env') -Export('env') + env.add_source_files(env.editor_sources, "*.cpp") diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index 19d5243776..205458fb1d 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -131,10 +131,6 @@ void AnimationNodeBlendTreeEditor::_update_graph() { Ref<AnimationNode> agnode = blend_tree->get_node(E->get()); - if (!agnode->is_connected("changed", this, "_node_changed")) { - agnode->connect("changed", this, "_node_changed", varray(agnode->get_instance_id()), CONNECT_DEFERRED); - } - node->set_offset(blend_tree->get_node_position(E->get()) * EDSCALE); node->set_title(agnode->get_caption()); @@ -721,14 +717,6 @@ void AnimationNodeBlendTreeEditor::_scroll_changed(const Vector2 &p_scroll) { updating = false; } -void AnimationNodeBlendTreeEditor::_node_changed(ObjectID p_node) { - - AnimationNode *an = Object::cast_to<AnimationNode>(ObjectDB::get_instance(p_node)); - //if (an && an->get_parent() == blend_tree) { - _update_graph(); - //} -} - void AnimationNodeBlendTreeEditor::_bind_methods() { ClassDB::bind_method("_update_graph", &AnimationNodeBlendTreeEditor::_update_graph); @@ -746,7 +734,6 @@ void AnimationNodeBlendTreeEditor::_bind_methods() { ClassDB::bind_method("_update_filters", &AnimationNodeBlendTreeEditor::_update_filters); ClassDB::bind_method("_filter_edited", &AnimationNodeBlendTreeEditor::_filter_edited); ClassDB::bind_method("_filter_toggled", &AnimationNodeBlendTreeEditor::_filter_toggled); - ClassDB::bind_method("_node_changed", &AnimationNodeBlendTreeEditor::_node_changed); ClassDB::bind_method("_removed_from_graph", &AnimationNodeBlendTreeEditor::_removed_from_graph); ClassDB::bind_method("_property_changed", &AnimationNodeBlendTreeEditor::_property_changed); ClassDB::bind_method("_file_opened", &AnimationNodeBlendTreeEditor::_file_opened); diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h index 9616e8b5da..e2daefdec6 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.h +++ b/editor/plugins/animation_blend_tree_editor_plugin.h @@ -104,8 +104,6 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { void _filter_toggled(); Ref<AnimationNode> _filter_edit; - void _node_changed(ObjectID p_node); - void _property_changed(const StringName &p_property, const Variant &p_value); void _removed_from_graph(); diff --git a/editor/plugins/baked_lightmap_editor_plugin.cpp b/editor/plugins/baked_lightmap_editor_plugin.cpp index 59b79bd070..e65a697857 100644 --- a/editor/plugins/baked_lightmap_editor_plugin.cpp +++ b/editor/plugins/baked_lightmap_editor_plugin.cpp @@ -50,6 +50,7 @@ void BakedLightmapEditorPlugin::_bake() { case BakedLightmap::BAKE_ERROR_CANT_CREATE_IMAGE: EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images, make sure path is writable.")); break; + default: {} } } } diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 7d971de0e3..f65c8cbd0d 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -2378,7 +2378,7 @@ void CanvasItemEditor::_draw_rulers() { if (i % minor_subdivision == 0) { viewport->draw_line(Point2(position.x, RULER_WIDTH * 0.33), Point2(position.x, RULER_WIDTH), graduation_color); } else { - viewport->draw_line(Point2(position.x, RULER_WIDTH * 0.66), Point2(position.x, RULER_WIDTH), graduation_color); + viewport->draw_line(Point2(position.x, RULER_WIDTH * 0.75), Point2(position.x, RULER_WIDTH), graduation_color); } } } @@ -2390,12 +2390,17 @@ void CanvasItemEditor::_draw_rulers() { if (i % (major_subdivision * minor_subdivision) == 0) { viewport->draw_line(Point2(0, position.y), Point2(RULER_WIDTH, position.y), graduation_color); float val = (ruler_transform * major_subdivide * minor_subdivide).xform(Point2(0, i)).y; - viewport->draw_string(font, Point2(2, position.y + 2 + font->get_height()), vformat(((int)val == val) ? "%d" : "%.1f", val), font_color); + + Transform2D text_xform = Transform2D(-Math_PI / 2.0, Point2(font->get_height(), position.y - 2)); + viewport->draw_set_transform_matrix(viewport->get_transform() * text_xform); + viewport->draw_string(font, Point2(), vformat(((int)val == val) ? "%d" : "%.1f", val), font_color); + viewport->draw_set_transform_matrix(viewport->get_transform()); + } else { if (i % minor_subdivision == 0) { viewport->draw_line(Point2(RULER_WIDTH * 0.33, position.y), Point2(RULER_WIDTH, position.y), graduation_color); } else { - viewport->draw_line(Point2(RULER_WIDTH * 0.66, position.y), Point2(RULER_WIDTH, position.y), graduation_color); + viewport->draw_line(Point2(RULER_WIDTH * 0.75, position.y), Point2(RULER_WIDTH, position.y), graduation_color); } } } diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp index e3f364790a..d1a94f5b49 100644 --- a/editor/plugins/collision_shape_2d_editor_plugin.cpp +++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp @@ -323,7 +323,6 @@ bool CollisionShape2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e if (mb.is_valid()) { Vector2 gpoint = mb->get_position(); - Vector2 cpoint = node->get_global_transform().affine_inverse().xform(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mb->get_position()))); if (mb->get_button_index() == BUTTON_LEFT) { if (mb->is_pressed()) { diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index 79169d3183..8b7156dcee 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -205,13 +205,13 @@ void CurveEditor::on_gui_input(const Ref<InputEvent> &p_event) { curve.set_point_left_tangent(_selected_point, tangent); // Note: if a tangent is set to linear, it shouldn't be linked to the other - if (link && _selected_point != curve.get_point_count() - 1 && !curve.get_point_right_mode(_selected_point) != Curve::TANGENT_FREE) + if (link && _selected_point != (curve.get_point_count() - 1) && curve.get_point_right_mode(_selected_point) != Curve::TANGENT_LINEAR) curve.set_point_right_tangent(_selected_point, tangent); } else { curve.set_point_right_tangent(_selected_point, tangent); - if (link && _selected_point != 0 && !curve.get_point_left_mode(_selected_point) != Curve::TANGENT_FREE) + if (link && _selected_point != 0 && curve.get_point_left_mode(_selected_point) != Curve::TANGENT_LINEAR) curve.set_point_left_tangent(_selected_point, tangent); } } diff --git a/editor/plugins/mesh_editor_plugin.cpp b/editor/plugins/mesh_editor_plugin.cpp index 7b7e23531a..73a216e96f 100644 --- a/editor/plugins/mesh_editor_plugin.cpp +++ b/editor/plugins/mesh_editor_plugin.cpp @@ -65,14 +65,6 @@ void MeshEditor::_notification(int p_what) { first_enter = false; } } - - if (p_what == NOTIFICATION_DRAW) { - - Ref<Texture> checkerboard = get_icon("Checkerboard", "EditorIcons"); - Size2 size = get_size(); - - //draw_texture_rect(checkerboard, Rect2(Point2(), size), true); - } } void MeshEditor::_update_rotation() { diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index e0c8cf41ff..f937744d45 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -767,6 +767,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { node->set_polygon(uv_new); } } break; + default: {} } if (bone_painting) { diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 5e000ca6ef..d2a9830fe0 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -1414,21 +1414,6 @@ void ScriptEditor::notify_script_changed(const Ref<Script> &p_script) { emit_signal("editor_script_changed", p_script); } -static const Node *_find_node_with_script(const Node *p_node, const RefPtr &p_script) { - - if (p_node->get_script() == p_script) - return p_node; - - for (int i = 0; i < p_node->get_child_count(); i++) { - - const Node *result = _find_node_with_script(p_node->get_child(i), p_script); - if (result) - return result; - } - - return NULL; -} - void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) { for (int i = 0; i < tab_container->get_child_count(); i++) { diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 46a6b85131..23babdf07b 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -1276,7 +1276,6 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { word_at_mouse = tx->get_selection_text(); bool has_color = (word_at_mouse == "Color"); - int fold_state = 0; bool foldable = tx->can_fold(row) || tx->is_folded(row); bool open_docs = false; bool goto_definition = false; diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 361271af89..17f93b55a1 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -86,10 +86,7 @@ void ShaderTextEditor::_load_theme_settings() { Color search_result_border_color = EDITOR_GET("text_editor/highlighting/search_result_border_color"); Color symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color"); Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color"); - Color basetype_color = EDITOR_GET("text_editor/highlighting/base_type_color"); - Color type_color = EDITOR_GET("text_editor/highlighting/engine_type_color"); Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color"); - Color string_color = EDITOR_GET("text_editor/highlighting/string_color"); get_text_edit()->add_color_override("background_color", background_color); get_text_edit()->add_color_override("completion_background_color", completion_background_color); @@ -140,26 +137,9 @@ void ShaderTextEditor::_load_theme_settings() { get_text_edit()->add_keyword_color(E->get(), keyword_color); } - //colorize core types - //Color basetype_color= EDITOR_DEF("text_editor/base_type_color",Color(0.3,0.3,0.0)); - //colorize comments get_text_edit()->add_color_region("/*", "*/", comment_color, false); get_text_edit()->add_color_region("//", "", comment_color, false); - - /*//colorize strings - Color string_color = EDITOR_DEF("text_editor/string_color",Color::hex(0x6b6f00ff)); - - List<String> strings; - shader->get_shader_mode()->get_string_delimiters(&strings); - - for (List<String>::Element *E=strings.front();E;E=E->next()) { - - String string = E->get(); - String beg = string.get_slice(" ",0); - String end = string.get_slice_count(" ")>1?string.get_slice(" ",1):String(); - get_text_edit()->add_color_region(beg,end,string_color,end==""); - }*/ } void ShaderTextEditor::_check_shader_mode() { @@ -468,7 +448,6 @@ void ShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { int col, row; TextEdit *tx = shader_editor->get_text_edit(); tx->_get_mouse_pos(mb->get_global_position() - tx->get_global_position(), row, col); - Vector2 mpos = mb->get_global_position() - tx->get_global_position(); tx->set_right_click_moves_caret(EditorSettings::get_singleton()->get("text_editor/cursor/right_click_moves_caret")); if (tx->is_right_click_moving_caret()) { diff --git a/editor/plugins/skeleton_editor_plugin.cpp b/editor/plugins/skeleton_editor_plugin.cpp index 50deb80668..e7d9f1b702 100644 --- a/editor/plugins/skeleton_editor_plugin.cpp +++ b/editor/plugins/skeleton_editor_plugin.cpp @@ -29,9 +29,10 @@ /*************************************************************************/ #include "skeleton_editor_plugin.h" + #include "scene/3d/collision_shape.h" #include "scene/3d/physics_body.h" -#include "scene/3d/physics_joint.h"; +#include "scene/3d/physics_joint.h" #include "scene/resources/capsule_shape.h" #include "scene/resources/sphere_shape.h" #include "spatial_editor_plugin.h" diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 3e6a0ae81a..b68ea71cd4 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -531,12 +531,12 @@ void SpatialEditorViewport::_select_region() { void SpatialEditorViewport::_update_name() { - String ortho = orthogonal ? TTR("Orthogonal") : TTR("Perspective"); + String view_mode = orthogonal ? TTR("Orthogonal") : TTR("Perspective"); if (name != "") - view_menu->set_text("[ " + name + " " + ortho + " ]"); + view_menu->set_text("[ " + name + " " + view_mode + " ]"); else - view_menu->set_text("[ " + ortho + " ]"); + view_menu->set_text("[ " + view_mode + " ]"); view_menu->set_size(Vector2(0, 0)); // resets the button size } @@ -997,6 +997,10 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { set_message(TTR("View Plane Transform."), 2); } break; + case TRANSFORM_YZ: + case TRANSFORM_XZ: + case TRANSFORM_XY: { + } break; } } } break; @@ -1545,6 +1549,10 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(2)); axis = Vector3(0, 0, 1); break; + case TRANSFORM_YZ: + case TRANSFORM_XZ: + case TRANSFORM_XY: + break; } Vector3 intersection; @@ -1904,7 +1912,7 @@ void SpatialEditorViewport::_nav_orbit(Ref<InputEventWithModifiers> p_event, con void SpatialEditorViewport::_nav_look(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative) { // Freelook only works properly in perspective. - // It technically works too in ortho, but it's awful for a user due to fov being near zero + // It could technically work in ortho, but it's terrible for a user due to FOV being a fixed width. if (!orthogonal) { real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity"); real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel); @@ -2075,7 +2083,7 @@ void SpatialEditorViewport::set_message(String p_message, float p_time) { } void SpatialEditorPlugin::edited_scene_changed() { - for (int i = 0; i < SpatialEditor::VIEWPORTS_COUNT; i++) { + for (uint32_t i = 0; i < SpatialEditor::VIEWPORTS_COUNT; i++) { SpatialEditorViewport *viewport = SpatialEditor::get_singleton()->get_editor_viewport(i); if (viewport->is_visible()) { viewport->notification(Control::NOTIFICATION_VISIBILITY_CHANGED); @@ -2199,7 +2207,7 @@ void SpatialEditorViewport::_notification(int p_what) { bool shrink = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION)); - if (shrink != viewport_container->get_stretch_shrink() > 1) { + if (shrink != (viewport_container->get_stretch_shrink() > 1)) { viewport_container->set_stretch_shrink(shrink ? 2 : 1); } @@ -4093,7 +4101,7 @@ void SpatialEditor::set_state(const Dictionary &p_state) { for (int j = 0; j < gizmo_plugins.size(); ++j) { if (!gizmo_plugins[j]->can_be_hidden()) continue; int state = EditorSpatialGizmoPlugin::ON_TOP; - for (uint32_t i = 0; i < keys.size(); i++) { + for (int i = 0; i < keys.size(); i++) { if (gizmo_plugins.write[j]->get_name() == keys[i]) { state = gizmos_status[keys[i]]; } @@ -4979,32 +4987,29 @@ void SpatialEditor::_unhandled_key_input(Ref<InputEvent> p_event) { if (!k->is_pressed()) return; - if (ED_IS_SHORTCUT("spatial_editor/tool_select", p_event)) + if (ED_IS_SHORTCUT("spatial_editor/tool_select", p_event)) { _menu_item_pressed(MENU_TOOL_SELECT); - - else if (ED_IS_SHORTCUT("spatial_editor/tool_move", p_event)) + } else if (ED_IS_SHORTCUT("spatial_editor/tool_move", p_event)) { _menu_item_pressed(MENU_TOOL_MOVE); - - else if (ED_IS_SHORTCUT("spatial_editor/tool_rotate", p_event)) + } else if (ED_IS_SHORTCUT("spatial_editor/tool_rotate", p_event)) { _menu_item_pressed(MENU_TOOL_ROTATE); - - else if (ED_IS_SHORTCUT("spatial_editor/tool_scale", p_event)) + } else if (ED_IS_SHORTCUT("spatial_editor/tool_scale", p_event)) { _menu_item_pressed(MENU_TOOL_SCALE); - else if (ED_IS_SHORTCUT("spatial_editor/snap_to_floor", p_event)) + } else if (ED_IS_SHORTCUT("spatial_editor/snap_to_floor", p_event)) { snap_selected_nodes_to_floor(); - - else if (ED_IS_SHORTCUT("spatial_editor/local_coords", p_event)) + } else if (ED_IS_SHORTCUT("spatial_editor/local_coords", p_event)) { if (are_local_coords_enabled()) { _menu_item_toggled(false, MENU_TOOL_LOCAL_COORDS); } else { _menu_item_toggled(true, MENU_TOOL_LOCAL_COORDS); } - else if (ED_IS_SHORTCUT("spatial_editor/snap", p_event)) + } else if (ED_IS_SHORTCUT("spatial_editor/snap", p_event)) { if (is_snap_enabled()) { _menu_item_toggled(false, MENU_TOOL_USE_SNAP); } else { _menu_item_toggled(true, MENU_TOOL_USE_SNAP); } + } } } } diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h index b7317cd593..773739d6e0 100644 --- a/editor/plugins/spatial_editor_plugin.h +++ b/editor/plugins/spatial_editor_plugin.h @@ -86,7 +86,7 @@ public: Vector<Vector3> handles; Vector<Vector3> secondary_handles; - float selectable_icon_size = -1.0f; + float selectable_icon_size; bool billboard_handle; bool valid; diff --git a/editor/plugins/sprite_editor_plugin.cpp b/editor/plugins/sprite_editor_plugin.cpp index 58a1835e68..c574b5e8ba 100644 --- a/editor/plugins/sprite_editor_plugin.cpp +++ b/editor/plugins/sprite_editor_plugin.cpp @@ -97,7 +97,7 @@ Vector<Vector2> expand(const Vector<Vector2> &points, const Rect2i &rect, float int lasti = p2->Contour.size() - 1; Vector2 prev = Vector2(p2->Contour[lasti].X / PRECISION, p2->Contour[lasti].Y / PRECISION); - for (int i = 0; i < p2->Contour.size(); i++) { + for (unsigned int i = 0; i < p2->Contour.size(); i++) { Vector2 cur = Vector2(p2->Contour[i].X / PRECISION, p2->Contour[i].Y / PRECISION); if (cur.distance_to(prev) > 0.5) { diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index a6a256f0d6..3de2284cea 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -795,6 +795,7 @@ void TileSetEditor::_on_workspace_draw() { spin_priority->set_suffix(" / " + String::num(total, 0)); draw_highlight_subtile(edited_shape_coord, queue_others); } break; + default: {} } draw_tile_subdivision(get_current_tile(), Color(0.347214, 0.722656, 0.617063)); @@ -1365,6 +1366,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } } } break; + default: {} } } } @@ -1434,6 +1436,7 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { workspace->update(); } } break; + default: {} } } } else if (p_tool == ZOOM_OUT) { @@ -1862,6 +1865,7 @@ void TileSetEditor::draw_polygon_shapes() { } } } break; + default: {} } if (creating_shape) { for (int j = 0; j < current_shape.size() - 1; j++) { diff --git a/editor/pvrtc_compress.cpp b/editor/pvrtc_compress.cpp index b1c847570c..30e78aa32b 100644 --- a/editor/pvrtc_compress.cpp +++ b/editor/pvrtc_compress.cpp @@ -115,11 +115,6 @@ static void _compress_pvrtc4(Image *p_image) { _compress_image(Image::COMPRESS_PVRTC4, p_image); } -static void _compress_etc(Image *p_image) { - - _compress_image(Image::COMPRESS_ETC, p_image); -} - void _pvrtc_register_compressors() { _base_image_compress_pvrtc2_func = Image::_image_compress_pvrtc2_func; @@ -127,5 +122,4 @@ void _pvrtc_register_compressors() { Image::_image_compress_pvrtc2_func = _compress_pvrtc2; Image::_image_compress_pvrtc4_func = _compress_pvrtc4; - //Image::_image_compress_etc_func=_compress_etc; //use the built in one for ETC } diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index ff6832177e..11a0a42b9e 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -568,15 +568,21 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { return; editor_data->get_undo_redo().create_action("Make node as Root"); - _node_replace_owner(root, node, node, MODE_DO); editor_data->get_undo_redo().add_do_method(node->get_parent(), "remove_child", node); + editor_data->get_undo_redo().add_do_method(root->get_parent(), "remove_child", root); + editor_data->get_undo_redo().add_do_method(node, "add_child", root); editor_data->get_undo_redo().add_do_method(editor, "set_edited_scene", node); editor_data->get_undo_redo().add_do_method(node, "set_filename", root->get_filename()); + editor_data->get_undo_redo().add_do_method(root, "set_filename", String()); + _node_replace_owner(root, root, node, MODE_DO); + editor_data->get_undo_redo().add_undo_method(root, "set_filename", root->get_filename()); editor_data->get_undo_redo().add_undo_method(node, "set_filename", String()); + editor_data->get_undo_redo().add_undo_method(node, "remove_child", root); editor_data->get_undo_redo().add_undo_method(editor, "set_edited_scene", root); editor_data->get_undo_redo().add_undo_method(node->get_parent(), "add_child", node); - _node_replace_owner(root, node, root, MODE_UNDO); + _node_replace_owner(root, root, root, MODE_UNDO); + editor_data->get_undo_redo().add_do_method(scene_tree, "update_tree"); editor_data->get_undo_redo().add_undo_method(scene_tree, "update_tree"); editor_data->get_undo_redo().add_undo_reference(root); @@ -690,21 +696,13 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { Node *node = e->get(); if (node) { bool editable = EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(node); - int editable_item_idx = menu->get_item_idx_from_text(TTR("Editable Children")); - int placeholder_item_idx = menu->get_item_idx_from_text(TTR("Load As Placeholder")); - editable = !editable; - EditorNode::get_singleton()->get_edited_scene()->set_editable_instance(node, editable); - - menu->set_item_checked(editable_item_idx, editable); if (editable) { - node->set_scene_instance_load_placeholder(false); - menu->set_item_checked(placeholder_item_idx, false); + editable_instance_remove_dialog->set_text(TTR("Disabling \"editable_instance\" will cause all properties of the node to be reverted to their default.")); + editable_instance_remove_dialog->popup_centered_minsize(); + break; } - - SpatialEditor::get_singleton()->update_all_gizmos(node); - - scene_tree->update_tree(); + _toggle_editable_children(); } } } break; @@ -975,24 +973,22 @@ void SceneTreeDock::_notification(int p_what) { void SceneTreeDock::_node_replace_owner(Node *p_base, Node *p_node, Node *p_root, ReplaceOwnerMode p_mode) { - if (p_base != p_node) { - if (p_node->get_owner() == p_base) { - UndoRedo *undo_redo = &editor_data->get_undo_redo(); - switch (p_mode) { - case MODE_BIDI: { - undo_redo->add_do_method(p_node, "set_owner", p_root); - undo_redo->add_undo_method(p_node, "set_owner", p_base); + if (p_node->get_owner() == p_base || !p_node->get_owner()) { + UndoRedo *undo_redo = &editor_data->get_undo_redo(); + switch (p_mode) { + case MODE_BIDI: { + undo_redo->add_do_method(p_node, "set_owner", p_root); + undo_redo->add_undo_method(p_node, "set_owner", p_base); - } break; - case MODE_DO: { - undo_redo->add_do_method(p_node, "set_owner", p_root); + } break; + case MODE_DO: { + undo_redo->add_do_method(p_node, "set_owner", p_root); - } break; - case MODE_UNDO: { - undo_redo->add_undo_method(p_node, "set_owner", p_root); + } break; + case MODE_UNDO: { + undo_redo->add_undo_method(p_node, "set_owner", p_root); - } break; - } + } break; } } @@ -1489,6 +1485,33 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) { editor->push_item(p_script.operator->()); } +void SceneTreeDock::_toggle_editable_children() { + List<Node *> selection = editor_selection->get_selected_node_list(); + List<Node *>::Element *e = selection.front(); + if (e) { + Node *node = e->get(); + if (node) { + bool editable = EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(node); + + int editable_item_idx = menu->get_item_idx_from_text(TTR("Editable Children")); + int placeholder_item_idx = menu->get_item_idx_from_text(TTR("Load As Placeholder")); + editable = !editable; + + EditorNode::get_singleton()->get_edited_scene()->set_editable_instance(node, editable); + + menu->set_item_checked(editable_item_idx, editable); + if (editable) { + node->set_scene_instance_load_placeholder(false); + menu->set_item_checked(placeholder_item_idx, false); + } + + SpatialEditor::get_singleton()->update_all_gizmos(node); + + scene_tree->update_tree(); + } + } +} + void SceneTreeDock::_delete_confirm() { List<Node *> remove_list = editor_selection->get_selected_node_list(); @@ -1570,7 +1593,7 @@ void SceneTreeDock::_delete_confirm() { // Fixes the EditorHistory from still offering deleted notes EditorHistory *editor_history = EditorNode::get_singleton()->get_editor_history(); editor_history->cleanup_history(); - EditorNode::get_singleton()->call("_prepare_history"); + EditorNode::get_singleton()->get_inspector_dock()->call("_prepare_history"); } void SceneTreeDock::_update_script_button() { @@ -1587,6 +1610,10 @@ void SceneTreeDock::_selection_changed() { if (selection_size > 1) { //automatically turn on multi-edit _tool_selected(TOOL_MULTI_EDIT); + } else if (selection_size == 1) { + editor->push_item(EditorNode::get_singleton()->get_editor_selection()->get_selected_node_list()[0]); + } else { + editor->push_item(NULL); } _update_script_button(); } @@ -1852,32 +1879,6 @@ static bool _has_visible_children(Node *p_node) { return false; } -static Node *_find_last_visible(Node *p_node) { - - Node *last = NULL; - - bool collapsed = p_node->is_displayed_folded(); - - if (!collapsed) { - for (int i = 0; i < p_node->get_child_count(); i++) { - if (_is_node_visible(p_node->get_child(i))) { - last = p_node->get_child(i); - } - } - } - - if (last) { - Node *lastc = _find_last_visible(last); - if (lastc) - last = lastc; - - } else { - last = p_node; - } - - return last; -} - void SceneTreeDock::_normalize_drop(Node *&to_node, int &to_pos, int p_type) { to_pos = -1; @@ -2262,6 +2263,7 @@ void SceneTreeDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_input"), &SceneTreeDock::_input); ClassDB::bind_method(D_METHOD("_nodes_drag_begin"), &SceneTreeDock::_nodes_drag_begin); ClassDB::bind_method(D_METHOD("_delete_confirm"), &SceneTreeDock::_delete_confirm); + ClassDB::bind_method(D_METHOD("_toggle_editable_children"), &SceneTreeDock::_toggle_editable_children); ClassDB::bind_method(D_METHOD("_node_prerenamed"), &SceneTreeDock::_node_prerenamed); ClassDB::bind_method(D_METHOD("_import_subscene"), &SceneTreeDock::_import_subscene); ClassDB::bind_method(D_METHOD("_selection_changed"), &SceneTreeDock::_selection_changed); @@ -2427,6 +2429,10 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel add_child(delete_dialog); delete_dialog->connect("confirmed", this, "_delete_confirm"); + editable_instance_remove_dialog = memnew(ConfirmationDialog); + add_child(editable_instance_remove_dialog); + editable_instance_remove_dialog->connect("confirmed", this, "_toggle_editable_children"); + import_subscene_dialog = memnew(EditorSubScene); add_child(import_subscene_dialog); import_subscene_dialog->connect("subscene_selected", this, "_import_subscene"); diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index 34a7c98d11..269b0b69db 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -121,6 +121,7 @@ class SceneTreeDock : public VBoxContainer { ScriptCreateDialog *script_create_dialog; AcceptDialog *accept; ConfirmationDialog *delete_dialog; + ConfirmationDialog *editable_instance_remove_dialog; ReparentDialog *reparent_dialog; EditorFileDialog *file; @@ -169,6 +170,8 @@ class SceneTreeDock : public VBoxContainer { void _delete_confirm(); + void _toggle_editable_children(); + void _node_prerenamed(Node *p_node, const String &p_new_name); void _nodes_drag_begin(); diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index cc477314e9..2d4af6c63d 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/script_editor_debugger.cpp @@ -30,6 +30,7 @@ #include "script_editor_debugger.h" +#include "core/io/marshalls.h" #include "core/project_settings.h" #include "core/ustring.h" #include "editor_node.h" @@ -501,8 +502,12 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da String str = var; var = str.substr(4, str.length()); - if (str.begins_with("PATH")) - var = ResourceLoader::load(var); + if (str.begins_with("PATH")) { + if (String(var).empty()) + var = RES(); + else + var = ResourceLoader::load(var); + } } debugObj->prop_values[pinfo.name] = var; @@ -576,11 +581,9 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da String hs = String(); if (v.get_type() == Variant::OBJECT) { + v = Object::cast_to<EncodedObjectAsID>(v)->get_object_id(); h = PROPERTY_HINT_OBJECT_ID; - String s = v; - s = s.replace("[", ""); - hs = s.get_slice(":", 0); - v = s.get_slice(":", 1).to_int(); + hs = "Object"; } variables->add_property("Locals/" + n, v, h, hs); @@ -597,11 +600,9 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da String hs = String(); if (v.get_type() == Variant::OBJECT) { + v = Object::cast_to<EncodedObjectAsID>(v)->get_object_id(); h = PROPERTY_HINT_OBJECT_ID; - String s = v; - s = s.replace("[", ""); - hs = s.get_slice(":", 0); - v = s.get_slice(":", 1).to_int(); + hs = "Object"; } variables->add_property("Members/" + n, v, h, hs); @@ -618,11 +619,9 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da String hs = String(); if (v.get_type() == Variant::OBJECT) { + v = Object::cast_to<EncodedObjectAsID>(v)->get_object_id(); h = PROPERTY_HINT_OBJECT_ID; - String s = v; - s = s.replace("[", ""); - hs = s.get_slice(":", 0); - v = s.get_slice(":", 1).to_int(); + hs = "Object"; } variables->add_property("Globals/" + n, v, h, hs); @@ -1294,9 +1293,6 @@ void ScriptEditorDebugger::stop() { EditorNode::get_singleton()->get_scene_tree_dock()->hide_remote_tree(); EditorNode::get_singleton()->get_scene_tree_dock()->hide_tab_buttons(); - Node *node = editor->get_scene_tree_dock()->get_tree_editor()->get_selected(); - editor->push_item(node); - if (hide_on_stop) { if (is_visible_in_tree()) EditorNode::get_singleton()->hide_bottom_panel(); diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp index fe384da75b..537c9ac6b8 100644 --- a/editor/settings_config_dialog.cpp +++ b/editor/settings_config_dialog.cpp @@ -361,7 +361,7 @@ void EditorSettingsDialog::_tabs_tab_changed(int p_tab) { void EditorSettingsDialog::_focus_current_search_box() { Control *tab = tabs->get_current_tab_control(); - LineEdit *current_search_box; + LineEdit *current_search_box = NULL; if (tab == tab_general) current_search_box = search_box; else if (tab == tab_shortcuts) diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp index 3097f0d0b9..96bca86f83 100644 --- a/editor/spatial_editor_gizmos.cpp +++ b/editor/spatial_editor_gizmos.cpp @@ -715,6 +715,7 @@ EditorSpatialGizmo::EditorSpatialGizmo() { instanced = false; spatial_node = NULL; gizmo_plugin = NULL; + selectable_icon_size = -1.0f; } EditorSpatialGizmo::~EditorSpatialGizmo() { diff --git a/gles_builders.py b/gles_builders.py index 8ed9f39393..1e63a53f1a 100644 --- a/gles_builders.py +++ b/gles_builders.py @@ -68,12 +68,9 @@ def include_file_in_legacygl_header(filename, header_data, depth): line = fs.readline() - if line.find("#ifdef ") != -1 or line.find("#elif defined(") != -1: + if line.find("#ifdef ") != -1: if line.find("#ifdef ") != -1: ifdefline = line.replace("#ifdef ", "").strip() - else: - ifdefline = line.replace("#elif defined(", "").strip() - ifdefline = ifdefline.replace(")", "").strip() if line.find("_EN_") != -1: enumbase = ifdefline[:ifdefline.find("_EN_")] diff --git a/main/SCsub b/main/SCsub index 9af102600e..e7fe6ab4e1 100644 --- a/main/SCsub +++ b/main/SCsub @@ -1,6 +1,7 @@ #!/usr/bin/env python Import('env') + from platform_methods import run_in_subprocess import main_builders @@ -15,8 +16,6 @@ env.CommandNoCache("#main/default_controller_mappings.gen.cpp", controller_datab env.main_sources.append("#main/default_controller_mappings.gen.cpp") -Export('env') - env.Depends("#main/splash.gen.h", "#main/splash.png") env.CommandNoCache("#main/splash.gen.h", "#main/splash.png", run_in_subprocess(main_builders.make_splash)) diff --git a/main/input_default.cpp b/main/input_default.cpp index 10be291b8d..913c143025 100644 --- a/main/input_default.cpp +++ b/main/input_default.cpp @@ -343,9 +343,9 @@ void InputDefault::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool button_event->set_pressed(st->is_pressed()); button_event->set_button_index(BUTTON_LEFT); if (st->is_pressed()) { - button_event->set_button_mask(mouse_button_mask | (1 << BUTTON_LEFT - 1)); + button_event->set_button_mask(mouse_button_mask | (1 << (BUTTON_LEFT - 1))); } else { - button_event->set_button_mask(mouse_button_mask & ~(1 << BUTTON_LEFT - 1)); + button_event->set_button_mask(mouse_button_mask & ~(1 << (BUTTON_LEFT - 1))); } _parse_input_event_impl(button_event, true); @@ -576,7 +576,7 @@ void InputDefault::ensure_touch_mouse_raised() { button_event->set_global_position(mouse_pos); button_event->set_pressed(false); button_event->set_button_index(BUTTON_LEFT); - button_event->set_button_mask(mouse_button_mask & ~(1 << BUTTON_LEFT - 1)); + button_event->set_button_mask(mouse_button_mask & ~(1 << (BUTTON_LEFT - 1))); _parse_input_event_impl(button_event, true); } @@ -636,6 +636,7 @@ InputDefault::InputDefault() { emulate_mouse_from_touch = false; mouse_from_touch_index = -1; main_loop = NULL; + default_shape = CURSOR_ARROW; hat_map_default[HAT_UP].type = TYPE_BUTTON; hat_map_default[HAT_UP].index = JOY_DPAD_UP; diff --git a/main/input_default.h b/main/input_default.h index 4441ade04e..b420ec124b 100644 --- a/main/input_default.h +++ b/main/input_default.h @@ -119,7 +119,8 @@ class InputDefault : public Input { SpeedTrack mouse_speed_track; Map<int, Joypad> joy_names; int fallback_mapping; - CursorShape default_shape = CURSOR_ARROW; + + CursorShape default_shape; public: enum HatMask { diff --git a/main/main.cpp b/main/main.cpp index dac646ba70..3eb0b7f354 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -34,6 +34,7 @@ #include "core/io/file_access_network.h" #include "core/io/file_access_pack.h" #include "core/io/file_access_zip.h" +#include "core/io/image_loader.h" #include "core/io/ip.h" #include "core/io/resource_loader.h" #include "core/io/stream_peer_ssl.h" @@ -129,7 +130,6 @@ static bool init_always_on_top = false; static bool init_use_custom_pos = false; static Vector2 init_custom_pos; static bool force_lowdpi = false; -static bool use_vsync = true; // Debug @@ -1125,9 +1125,8 @@ Error Main::setup2(Thread::ID p_main_tid_override) { boot_logo_path = boot_logo_path.strip_edges(); if (boot_logo_path != String()) { - print_line("Boot splash path: " + boot_logo_path); boot_logo.instance(); - Error err = boot_logo->load(boot_logo_path); + Error err = ImageLoader::load_image(boot_logo_path, boot_logo); if (err) ERR_PRINTS("Non-existing or invalid boot splash at: " + boot_logo_path + ". Loading default splash."); } @@ -1708,7 +1707,7 @@ bool Main::start() { if (iconpath != "") { Ref<Image> icon; icon.instance(); - if (icon->load(iconpath) == OK) { + if (ImageLoader::load_image(iconpath, icon) == OK) { OS::get_singleton()->set_icon(icon); hasicon = true; } diff --git a/main/tests/SCsub b/main/tests/SCsub index 26a0819ee8..437d9ed777 100644 --- a/main/tests/SCsub +++ b/main/tests/SCsub @@ -5,9 +5,5 @@ Import('env') env.tests_sources = [] env.add_source_files(env.tests_sources, "*.cpp") -Export('env') - -# SConscript('math/SCsub'); - lib = env.add_library("tests", env.tests_sources) env.Prepend(LIBS=[lib]) diff --git a/main/tests/test_gdscript.cpp b/main/tests/test_gdscript.cpp index 412e809732..4d2fa2a26d 100644 --- a/main/tests/test_gdscript.cpp +++ b/main/tests/test_gdscript.cpp @@ -357,6 +357,9 @@ static void _parser_show_block(const GDScriptParser::BlockNode *p_block, int p_i _parser_show_block(cf_node->body, p_indent + 1); } break; + case GDScriptParser::ControlFlowNode::CF_MATCH: { + // FIXME: Implement + } break; case GDScriptParser::ControlFlowNode::CF_SWITCH: { } break; diff --git a/main/tests/test_gui.cpp b/main/tests/test_gui.cpp index d25eedbf6f..271353f1dd 100644 --- a/main/tests/test_gui.cpp +++ b/main/tests/test_gui.cpp @@ -60,8 +60,6 @@ namespace TestGUI { class TestMainLoop : public SceneTree { - Control *control; - public: virtual void request_quit() { diff --git a/main/tests/test_shader_lang.cpp b/main/tests/test_shader_lang.cpp index 2cd39d0208..9df5973376 100644 --- a/main/tests/test_shader_lang.cpp +++ b/main/tests/test_shader_lang.cpp @@ -194,6 +194,9 @@ static String dump_node_code(SL::Node *p_node, int p_level) { code = vnode->name; } break; + case SL::Node::TYPE_VARIABLE_DECLARATION: { + // FIXME: Implement + } break; case SL::Node::TYPE_CONSTANT: { SL::ConstantNode *cnode = (SL::ConstantNode *)p_node; return get_constant_text(cnode->datatype, cnode->values); diff --git a/methods.py b/methods.py index 00c477635e..3add9b1f18 100644 --- a/methods.py +++ b/methods.py @@ -19,6 +19,14 @@ def add_source_files(self, sources, filetype, lib_env=None, shared=False): sources.append(self.Object(path)) +def disable_warnings(self): + # 'self' is the environment + if self.msvc: + self.Append(CCFLAGS=['/w']) + else: + self.Append(CCFLAGS=['-w']) + + def add_module_version_string(self,s): self.module_version_string += "." + s diff --git a/modules/SCsub b/modules/SCsub index 74a5267355..67f5893db4 100644 --- a/modules/SCsub +++ b/modules/SCsub @@ -9,7 +9,6 @@ Export('env_modules') env.modules_sources = [ "register_module_types.gen.cpp", ] -Export('env') for x in env.module_list: if (x in env.disabled_modules): @@ -20,7 +19,6 @@ for x in env.module_list: if env.split_modules: env.split_lib("modules", env_lib = env_modules) else: - lib = env_modules.add_library("modules", env.modules_sources) env.Prepend(LIBS=[lib]) diff --git a/modules/bullet/SCsub b/modules/bullet/SCsub index 2557e8cb1d..11ce18449b 100644 --- a/modules/bullet/SCsub +++ b/modules/bullet/SCsub @@ -186,8 +186,12 @@ if env['builtin_bullet']: thirdparty_sources = [thirdparty_dir + file for file in bullet2_src] - env_bullet.add_source_files(env.modules_sources, thirdparty_sources) env_bullet.Append(CPPPATH=[thirdparty_dir]) + env_thirdparty = env_bullet.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + + # Godot source files env_bullet.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp index 3200b4a214..a3ba3aa0bf 100644 --- a/modules/bullet/area_bullet.cpp +++ b/modules/bullet/area_bullet.cpp @@ -46,7 +46,6 @@ AreaBullet::AreaBullet() : RigidCollisionObjectBullet(CollisionObjectBullet::TYPE_AREA), monitorable(true), - isScratched(false), spOv_mode(PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED), spOv_gravityPoint(false), spOv_gravityPointDistanceScale(0), @@ -55,7 +54,8 @@ AreaBullet::AreaBullet() : spOv_gravityMag(10), spOv_linearDump(0.1), spOv_angularDump(1), - spOv_priority(0) { + spOv_priority(0), + isScratched(false) { btGhost = bulletnew(btGhostObject); btGhost->setCollisionShape(BulletPhysicsServer::get_empty_shape()); @@ -94,6 +94,9 @@ void AreaBullet::dispatch_callbacks() { otherObj.object->on_exit_area(this); overlappingObjects.remove(i); // Remove after callback break; + case OVERLAP_STATE_DIRTY: + case OVERLAP_STATE_INSIDE: + break; } } } diff --git a/modules/bullet/bullet_types_converter.cpp b/modules/bullet/bullet_types_converter.cpp index a0fe598227..f9b7126173 100644 --- a/modules/bullet/bullet_types_converter.cpp +++ b/modules/bullet/bullet_types_converter.cpp @@ -28,8 +28,6 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#pragma once - #include "bullet_types_converter.h" /** diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp index df67f8d7ab..61834b8e3f 100644 --- a/modules/bullet/collision_object_bullet.cpp +++ b/modules/bullet/collision_object_bullet.cpp @@ -66,13 +66,13 @@ void CollisionObjectBullet::ShapeWrapper::claim_bt_shape(const btVector3 &body_s CollisionObjectBullet::CollisionObjectBullet(Type p_type) : RIDBullet(), - space(NULL), type(p_type), collisionsEnabled(true), m_isStatic(false), bt_collision_object(NULL), body_scale(1., 1., 1.), - force_shape_reset(false) {} + force_shape_reset(false), + space(NULL) {} CollisionObjectBullet::~CollisionObjectBullet() { // Remove all overlapping, notify is not required since godot take care of it diff --git a/modules/bullet/godot_ray_world_algorithm.cpp b/modules/bullet/godot_ray_world_algorithm.cpp index 53d0ab7e3c..27ee44d1bd 100644 --- a/modules/bullet/godot_ray_world_algorithm.cpp +++ b/modules/bullet/godot_ray_world_algorithm.cpp @@ -49,9 +49,9 @@ GodotRayWorldAlgorithm::SwappedCreateFunc::SwappedCreateFunc(const btDiscreteDyn GodotRayWorldAlgorithm::GodotRayWorldAlgorithm(const btDiscreteDynamicsWorld *world, btPersistentManifold *mf, const btCollisionAlgorithmConstructionInfo &ci, const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap, bool isSwapped) : btActivatingCollisionAlgorithm(ci, body0Wrap, body1Wrap), + m_world(world), m_manifoldPtr(mf), m_ownManifold(false), - m_world(world), m_isSwapped(isSwapped) {} GodotRayWorldAlgorithm::~GodotRayWorldAlgorithm() { diff --git a/modules/bullet/godot_result_callbacks.h b/modules/bullet/godot_result_callbacks.h index 8e70b72841..73e1fc9627 100644 --- a/modules/bullet/godot_result_callbacks.h +++ b/modules/bullet/godot_result_callbacks.h @@ -87,13 +87,13 @@ struct GodotAllConvexResultCallback : public btCollisionWorld::ConvexResultCallb public: PhysicsDirectSpaceState::ShapeResult *m_results; int m_resultMax; - int count; const Set<RID> *m_exclude; + int count; GodotAllConvexResultCallback(PhysicsDirectSpaceState::ShapeResult *p_results, int p_resultMax, const Set<RID> *p_exclude) : m_results(p_results), - m_exclude(p_exclude), m_resultMax(p_resultMax), + m_exclude(p_exclude), count(0) {} virtual bool needsCollision(btBroadphaseProxy *proxy0) const; @@ -125,6 +125,7 @@ public: GodotClosestConvexResultCallback(const btVector3 &convexFromWorld, const btVector3 &convexToWorld, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) : btCollisionWorld::ClosestConvexResultCallback(convexFromWorld, convexToWorld), m_exclude(p_exclude), + m_shapeId(0), collide_with_bodies(p_collide_with_bodies), collide_with_areas(p_collide_with_areas) {} @@ -138,8 +139,8 @@ public: const btCollisionObject *m_self_object; PhysicsDirectSpaceState::ShapeResult *m_results; int m_resultMax; - int m_count; const Set<RID> *m_exclude; + int m_count; bool collide_with_bodies; bool collide_with_areas; @@ -147,8 +148,8 @@ public: GodotAllContactResultCallback(btCollisionObject *p_self_object, PhysicsDirectSpaceState::ShapeResult *p_results, int p_resultMax, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) : m_self_object(p_self_object), m_results(p_results), - m_exclude(p_exclude), m_resultMax(p_resultMax), + m_exclude(p_exclude), m_count(0), collide_with_bodies(p_collide_with_bodies), collide_with_areas(p_collide_with_areas) {} @@ -164,8 +165,8 @@ public: const btCollisionObject *m_self_object; Vector3 *m_results; int m_resultMax; - int m_count; const Set<RID> *m_exclude; + int m_count; bool collide_with_bodies; bool collide_with_areas; @@ -173,8 +174,8 @@ public: GodotContactPairContactResultCallback(btCollisionObject *p_self_object, Vector3 *p_results, int p_resultMax, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) : m_self_object(p_self_object), m_results(p_results), - m_exclude(p_exclude), m_resultMax(p_resultMax), + m_exclude(p_exclude), m_count(0), collide_with_bodies(p_collide_with_bodies), collide_with_areas(p_collide_with_areas) {} @@ -188,11 +189,11 @@ struct GodotRestInfoContactResultCallback : public btCollisionWorld::ContactResu public: const btCollisionObject *m_self_object; PhysicsDirectSpaceState::ShapeRestInfo *m_result; + const Set<RID> *m_exclude; bool m_collided; real_t m_min_distance; const btCollisionObject *m_rest_info_collision_object; btVector3 m_rest_info_bt_point; - const Set<RID> *m_exclude; bool collide_with_bodies; bool collide_with_areas; diff --git a/modules/bullet/hinge_joint_bullet.cpp b/modules/bullet/hinge_joint_bullet.cpp index 86c6a632cd..3a4459a581 100644 --- a/modules/bullet/hinge_joint_bullet.cpp +++ b/modules/bullet/hinge_joint_bullet.cpp @@ -117,7 +117,7 @@ void HingeJointBullet::set_param(PhysicsServer::HingeJointParam p_param, real_t hingeConstraint->setMaxMotorImpulse(p_value); break; default: - ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated"); + ERR_EXPLAIN("The HingeJoint parameter " + itos(p_param) + " is deprecated."); WARN_DEPRECATED break; } @@ -143,7 +143,7 @@ real_t HingeJointBullet::get_param(PhysicsServer::HingeJointParam p_param) const case PhysicsServer::HINGE_JOINT_MOTOR_MAX_IMPULSE: return hingeConstraint->getMaxMotorImpulse(); default: - ERR_EXPLAIN("This parameter " + itos(p_param) + " is deprecated"); + ERR_EXPLAIN("The HingeJoint parameter " + itos(p_param) + " is deprecated."); WARN_DEPRECATED; return 0; } @@ -159,6 +159,7 @@ void HingeJointBullet::set_flag(PhysicsServer::HingeJointFlag p_flag, bool p_val case PhysicsServer::HINGE_JOINT_FLAG_ENABLE_MOTOR: hingeConstraint->enableMotor(p_value); break; + case PhysicsServer::HINGE_JOINT_FLAG_MAX: break; // Can't happen, but silences warning } } diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp index f24c8670a3..d9a77885b3 100644 --- a/modules/bullet/rigid_body_bullet.cpp +++ b/modules/bullet/rigid_body_bullet.cpp @@ -259,21 +259,21 @@ RigidBodyBullet::RigidBodyBullet() : RigidCollisionObjectBullet(CollisionObjectBullet::TYPE_RIGID_BODY), kinematic_utilities(NULL), locked_axis(0), - gravity_scale(1), mass(1), + gravity_scale(1), linearDamp(0), angularDamp(0), can_sleep(true), omit_forces_integration(false), - force_integration_callback(NULL), - isTransformChanged(false), - previousActiveState(true), maxCollisionsDetection(0), collisionsCount(0), maxAreasWhereIam(10), areaWhereIamCount(0), countGravityPointSpaces(0), - isScratchedSpaceOverrideModificator(false) { + isScratchedSpaceOverrideModificator(false), + isTransformChanged(false), + previousActiveState(true), + force_integration_callback(NULL) { godotMotionState = bulletnew(GodotMotionState(this)); @@ -535,20 +535,18 @@ void RigidBodyBullet::set_mode(PhysicsServer::BodyMode p_mode) { reload_axis_lock(); _internal_set_mass(0); break; - case PhysicsServer::BODY_MODE_RIGID: { + case PhysicsServer::BODY_MODE_RIGID: mode = PhysicsServer::BODY_MODE_RIGID; reload_axis_lock(); _internal_set_mass(0 == mass ? 1 : mass); scratch_space_override_modificator(); break; - } - case PhysicsServer::BODY_MODE_CHARACTER: { + case PhysicsServer::BODY_MODE_CHARACTER: mode = PhysicsServer::BODY_MODE_CHARACTER; reload_axis_lock(); _internal_set_mass(0 == mass ? 1 : mass); scratch_space_override_modificator(); break; - } } btBody->setAngularVelocity(btVector3(0, 0, 0)); @@ -927,10 +925,10 @@ void RigidBodyBullet::reload_space_override_modificator() { } switch (currentArea->get_spOv_mode()) { - ///case PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED: - /// This area does not affect gravity/damp. These are generally areas - /// that exist only to detect collisions, and objects entering or exiting them. - /// break; + case PhysicsServer::AREA_SPACE_OVERRIDE_DISABLED: + /// This area does not affect gravity/damp. These are generally areas + /// that exist only to detect collisions, and objects entering or exiting them. + break; case PhysicsServer::AREA_SPACE_OVERRIDE_COMBINE: /// This area adds its gravity/damp values to whatever has been /// calculated so far. This way, many overlapping areas can combine diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h index cd2f215906..25dac30951 100644 --- a/modules/bullet/rigid_body_bullet.h +++ b/modules/bullet/rigid_body_bullet.h @@ -227,7 +227,7 @@ public: void init_kinematic_utilities(); void destroy_kinematic_utilities(); - _FORCE_INLINE_ class KinematicUtilities *get_kinematic_utilities() const { return kinematic_utilities; } + _FORCE_INLINE_ KinematicUtilities *get_kinematic_utilities() const { return kinematic_utilities; } _FORCE_INLINE_ btRigidBody *get_bt_rigid_body() { return btBody; } diff --git a/modules/bullet/slider_joint_bullet.cpp b/modules/bullet/slider_joint_bullet.cpp index 9e1cd23989..9016ec3bf5 100644 --- a/modules/bullet/slider_joint_bullet.cpp +++ b/modules/bullet/slider_joint_bullet.cpp @@ -366,6 +366,7 @@ void SliderJointBullet::set_param(PhysicsServer::SliderJointParam p_param, real_ case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_SOFTNESS: setSoftnessOrthoAng(p_value); break; case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_RESTITUTION: setRestitutionOrthoAng(p_value); break; case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_DAMPING: setDampingOrthoAng(p_value); break; + case PhysicsServer::SLIDER_JOINT_MAX: break; // Can't happen, but silences warning } } diff --git a/modules/bullet/soft_body_bullet.cpp b/modules/bullet/soft_body_bullet.cpp index 9fc7230f91..f373ce5db4 100644 --- a/modules/bullet/soft_body_bullet.cpp +++ b/modules/bullet/soft_body_bullet.cpp @@ -37,17 +37,17 @@ SoftBodyBullet::SoftBodyBullet() : CollisionObjectBullet(CollisionObjectBullet::TYPE_SOFT_BODY), - total_mass(1), + bt_soft_body(NULL), + isScratched(false), simulation_precision(5), + total_mass(1.), linear_stiffness(0.5), areaAngular_stiffness(0.5), volume_stiffness(0.5), pressure_coefficient(0.), pose_matching_coefficient(0.), damping_coefficient(0.01), - drag_coefficient(0.), - bt_soft_body(NULL), - isScratched(false) {} + drag_coefficient(0.) {} SoftBodyBullet::~SoftBodyBullet() { } diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp index 329e12cfff..ba4c72f4c7 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -332,16 +332,17 @@ Vector3 BulletPhysicsDirectSpaceState::get_closest_point_to_object_volume(RID p_ SpaceBullet::SpaceBullet() : broadphase(NULL), + collisionConfiguration(NULL), dispatcher(NULL), solver(NULL), - collisionConfiguration(NULL), dynamicsWorld(NULL), soft_body_world_info(NULL), ghostPairCallback(NULL), godotFilterCallback(NULL), gravityDirection(0, -1, 0), gravityMagnitude(10), - contactDebugCount(0) { + contactDebugCount(0), + delta_time(0.) { create_empty_world(GLOBAL_DEF("physics/3d/active_soft_world", true)); direct_access = memnew(BulletPhysicsDirectSpaceState(this)); diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h index 67ab5c610d..c3d55cbbb1 100644 --- a/modules/bullet/space_bullet.h +++ b/modules/bullet/space_bullet.h @@ -57,7 +57,7 @@ class btDiscreteDynamicsWorld; class btEmptyShape; class btGhostPairCallback; class btSoftRigidDynamicsWorld; -class btSoftBodyWorldInfo; +struct btSoftBodyWorldInfo; class ConstraintBullet; class CollisionObjectBullet; class RigidBodyBullet; @@ -96,9 +96,9 @@ class SpaceBullet : public RIDBullet { btCollisionDispatcher *dispatcher; btConstraintSolver *solver; btDiscreteDynamicsWorld *dynamicsWorld; + btSoftBodyWorldInfo *soft_body_world_info; btGhostPairCallback *ghostPairCallback; GodotFilterCallback *godotFilterCallback; - btSoftBodyWorldInfo *soft_body_world_info; btGjkEpaPenetrationDepthSolver *gjk_epa_pen_solver; btVoronoiSimplexSolver *gjk_simplex_solver; diff --git a/modules/cvtt/SCsub b/modules/cvtt/SCsub index 5c396482aa..fcc69d8371 100644 --- a/modules/cvtt/SCsub +++ b/modules/cvtt/SCsub @@ -14,8 +14,11 @@ if env['builtin_squish']: thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_cvtt.add_source_files(env.modules_sources, thirdparty_sources) env_cvtt.Append(CPPPATH=[thirdparty_dir]) + env_thirdparty = env_cvtt.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot source files env_cvtt.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp index 17af6bff09..732b9cf733 100644 --- a/modules/cvtt/image_compress_cvtt.cpp +++ b/modules/cvtt/image_compress_cvtt.cpp @@ -118,7 +118,7 @@ static void _digest_row_task(const CVTTCompressionJobParams &p_job_params, const cvtt::Kernels::EncodeBC7(output_blocks, input_blocks_ldr, p_job_params.options); } - int num_real_blocks = ((w - x_start) + 3) / 4; + unsigned int num_real_blocks = ((w - x_start) + 3) / 4; if (num_real_blocks > cvtt::NumParallelBlocks) { num_real_blocks = cvtt::NumParallelBlocks; } @@ -131,7 +131,7 @@ static void _digest_row_task(const CVTTCompressionJobParams &p_job_params, const static void _digest_job_queue(void *p_job_queue) { CVTTCompressionJobQueue *job_queue = static_cast<CVTTCompressionJobQueue *>(p_job_queue); - for (int next_task = atomic_increment(&job_queue->current_task); next_task <= job_queue->num_tasks; next_task = atomic_increment(&job_queue->current_task)) { + for (uint32_t next_task = atomic_increment(&job_queue->current_task); next_task <= job_queue->num_tasks; next_task = atomic_increment(&job_queue->current_task)) { _digest_row_task(job_queue->job_params, job_queue->job_tasks[next_task - 1]); } } @@ -228,8 +228,6 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::CompressS uint8_t *out_bytes = &wb[dst_ofs]; for (int y_start = 0; y_start < h; y_start += 4) { - int y_end = y_start + 4; - CVTTCompressionRowTask row_task; row_task.width = w; row_task.height = h; @@ -308,7 +306,6 @@ void image_decompress_cvtt(Image *p_image) { int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps()); int mm_count = p_image->get_mipmap_count(); data.resize(target_size); - int shift = Image::get_format_pixel_rshift(target_format); PoolVector<uint8_t>::Write wb = data.write(); @@ -335,7 +332,7 @@ void image_decompress_cvtt(Image *p_image) { uint8_t input_blocks[16 * cvtt::NumParallelBlocks]; memset(input_blocks, 0, sizeof(input_blocks)); - int num_real_blocks = ((w - x_start) + 3) / 4; + unsigned int num_real_blocks = ((w - x_start) + 3) / 4; if (num_real_blocks > cvtt::NumParallelBlocks) { num_real_blocks = cvtt::NumParallelBlocks; } diff --git a/modules/enet/SCsub b/modules/enet/SCsub index 7caeafa1d6..a57a4b29ea 100644 --- a/modules/enet/SCsub +++ b/modules/enet/SCsub @@ -21,8 +21,11 @@ if env['builtin_enet']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_enet.add_source_files(env.modules_sources, thirdparty_sources) env_enet.Append(CPPPATH=[thirdparty_dir]) env_enet.Append(CPPFLAGS=["-DGODOT_ENET"]) + env_thirdparty = env_enet.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + env_enet.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/etc/SCsub b/modules/etc/SCsub index 31d8f00ef3..d2c77d6e3c 100644 --- a/modules/etc/SCsub +++ b/modules/etc/SCsub @@ -27,16 +27,20 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_etc.add_source_files(env.modules_sources, thirdparty_sources) env_etc.Append(CPPPATH=[thirdparty_dir]) -# Godot source files -env_etc.add_source_files(env.modules_sources, "*.cpp") - # upstream uses c++11 -if (not env_etc.msvc): +if not env.msvc: env_etc.Append(CCFLAGS="-std=c++11") -# -ffast-math seems to be incompatible with ec2comp on recent versions of + +# -ffast-math seems to be incompatible with etc2comp on recent versions of # GCC and Clang if '-ffast-math' in env_etc['CCFLAGS']: env_etc['CCFLAGS'].remove('-ffast-math') + +env_thirdparty = env_etc.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + +# Godot source files +env_etc.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/etc/image_etc.cpp b/modules/etc/image_etc.cpp index a534aec11b..fbbc765bf2 100644 --- a/modules/etc/image_etc.cpp +++ b/modules/etc/image_etc.cpp @@ -47,13 +47,14 @@ static Image::Format _get_etc2_mode(Image::DetectChannels format) { case Image::DETECTED_RGB: return Image::FORMAT_ETC2_RGB8; - default: + case Image::DETECTED_RGBA: return Image::FORMAT_ETC2_RGBA8; - // TODO: would be nice if we could use FORMAT_ETC2_RGB8A1 for FORMAT_RGBA5551 + // TODO: would be nice if we could use FORMAT_ETC2_RGB8A1 for FORMAT_RGBA5551 + default: + // TODO: Kept for compatibility, but should be investigated whether it's correct or if it should error out + return Image::FORMAT_ETC2_RGBA8; } - - ERR_FAIL_COND_V(true, Image::FORMAT_MAX); } static Etc::Image::Format _image_format_to_etc2comp_format(Image::Format format) { @@ -81,17 +82,10 @@ static Etc::Image::Format _image_format_to_etc2comp_format(Image::Format format) case Image::FORMAT_ETC2_RGB8A1: return Etc::Image::Format::RGB8A1; - } - - ERR_FAIL_COND_V(true, Etc::Image::Format::UNKNOWN); -} - -static void _decompress_etc1(Image *p_img) { - // not implemented, to be removed -} -static void _decompress_etc2(Image *p_img) { - // not implemented, to be removed + default: + ERR_FAIL_V(Etc::Image::Format::UNKNOWN); + } } static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_format, Image::CompressSource p_source) { @@ -174,7 +168,7 @@ static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_f PoolVector<uint8_t>::Read r = img->get_data().read(); - int target_size = Image::get_image_data_size(imgw, imgh, etc_format, p_img->has_mipmaps()); + unsigned int target_size = Image::get_image_data_size(imgw, imgh, etc_format, p_img->has_mipmaps()); int mmc = 1 + (p_img->has_mipmaps() ? Image::get_image_required_mipmaps(imgw, imgh, etc_format) : 0); PoolVector<uint8_t> dst_data; @@ -243,8 +237,5 @@ static void _compress_etc2(Image *p_img, float p_lossy_quality, Image::CompressS void _register_etc_compress_func() { Image::_image_compress_etc1_func = _compress_etc1; - //Image::_image_decompress_etc1 = _decompress_etc1; - Image::_image_compress_etc2_func = _compress_etc2; - //Image::_image_decompress_etc2 = _decompress_etc2; } diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub index c86e78ccee..7ca40c1b8b 100644 --- a/modules/freetype/SCsub +++ b/modules/freetype/SCsub @@ -1,9 +1,11 @@ #!/usr/bin/env python Import('env') +Import('env_modules') + from compat import isbasestring -# Not building in a separate env as scene needs it +env_freetype = env_modules.Clone() # Thirdparty source files if env['builtin_freetype']: @@ -54,26 +56,33 @@ if env['builtin_freetype']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - sfnt = thirdparty_dir + 'src/sfnt/sfnt.c' - - if 'platform' in env: - if env['platform'] == 'uwp': - # Include header for UWP to fix build issues - env.Append(CCFLAGS=['/FI', '"modules/freetype/uwpdef.h"']) - elif env['platform'] == 'javascript': - # Forcibly undefine this macro so SIMD is not used in this file, - # since currently unsupported in WASM - sfnt = env.Object(sfnt, CPPFLAGS=['-U__OPTIMIZE__']) + if env['platform'] == 'uwp': + # Include header for UWP to fix build issues + env_freetype.Append(CCFLAGS=['/FI', '"modules/freetype/uwpdef.h"']) + sfnt = thirdparty_dir + 'src/sfnt/sfnt.c' + if env['platform'] == 'javascript': + # Forcibly undefine this macro so SIMD is not used in this file, + # since currently unsupported in WASM + sfnt = env_freetype.Object(sfnt, CPPFLAGS=['-U__OPTIMIZE__']) thirdparty_sources += [sfnt] - env.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "/include"]) + env_freetype.Append(CPPPATH=[thirdparty_dir + "/include"]) + # Also needed in main env for scene/ + env.Append(CPPPATH=[thirdparty_dir + "/include"]) - # also requires libpng headers + env_freetype.Append(CCFLAGS=['-DFT2_BUILD_LIBRARY', '-DFT_CONFIG_OPTION_USE_PNG']) + if (env['target'] != 'release'): + env_freetype.Append(CCFLAGS=['-DZLIB_DEBUG']) + + # Also requires libpng headers if env['builtin_libpng']: - env.Append(CPPPATH=["#thirdparty/libpng"]) + env_freetype.Append(CPPPATH=["#thirdparty/libpng"]) + + env_thirdparty = env_freetype.Clone() + env_thirdparty.disable_warnings() + lib = env_thirdparty.add_library("freetype_builtin", thirdparty_sources) - lib = env.add_library("freetype_builtin", thirdparty_sources) # Needs to be appended to arrive after libscene in the linker call, # but we don't want it to arrive *after* system libs, so manual hack # LIBS contains first SCons Library objects ("SCons.Node.FS.File object") @@ -86,12 +95,8 @@ if env['builtin_freetype']: break if not inserted: env.Append(LIBS=[lib]) - env.Append(CCFLAGS=['-DFT2_BUILD_LIBRARY']) - if (env['target'] != 'release'): - env.Append(CCFLAGS=['-DZLIB_DEBUG']) # Godot source files -env.add_source_files(env.modules_sources, "*.cpp") -env.Append(CCFLAGS=['-DFREETYPE_ENABLED', '-DFT_CONFIG_OPTION_USE_PNG']) - -Export('env') +env_freetype.add_source_files(env.modules_sources, "*.cpp") +# Used in scene/, needs to be in main env +env.Append(CCFLAGS=['-DFREETYPE_ENABLED']) diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub index 46b2a832f1..fe2d8c7ce9 100644 --- a/modules/gdnative/SCsub +++ b/modules/gdnative/SCsub @@ -1,35 +1,38 @@ #!/usr/bin/env python Import('env') +Import('env_modules') -gdn_env = env.Clone() -gdn_env.add_source_files(env.modules_sources, "gdnative.cpp") -gdn_env.add_source_files(env.modules_sources, "register_types.cpp") -gdn_env.add_source_files(env.modules_sources, "android/*.cpp") -gdn_env.add_source_files(env.modules_sources, "gdnative/*.cpp") -gdn_env.add_source_files(env.modules_sources, "nativescript/*.cpp") -gdn_env.add_source_files(env.modules_sources, "gdnative_library_singleton_editor.cpp") -gdn_env.add_source_files(env.modules_sources, "gdnative_library_editor_plugin.cpp") +env_gdnative = env_modules.Clone() +env_gdnative.add_source_files(env.modules_sources, "gdnative.cpp") +env_gdnative.add_source_files(env.modules_sources, "register_types.cpp") +env_gdnative.add_source_files(env.modules_sources, "android/*.cpp") +env_gdnative.add_source_files(env.modules_sources, "gdnative/*.cpp") +env_gdnative.add_source_files(env.modules_sources, "nativescript/*.cpp") +env_gdnative.add_source_files(env.modules_sources, "gdnative_library_singleton_editor.cpp") +env_gdnative.add_source_files(env.modules_sources, "gdnative_library_editor_plugin.cpp") -gdn_env.Append(CPPPATH=['#modules/gdnative/include/']) +env_gdnative.Append(CPPPATH=['#modules/gdnative/include/']) + +Export('env_gdnative') SConscript("net/SCsub") SConscript("arvr/SCsub") SConscript("pluginscript/SCsub") + from platform_methods import run_in_subprocess import gdnative_builders - -_, gensource = gdn_env.CommandNoCache(['include/gdnative_api_struct.gen.h', 'gdnative_api_struct.gen.cpp'], +_, gensource = env_gdnative.CommandNoCache(['include/gdnative_api_struct.gen.h', 'gdnative_api_struct.gen.cpp'], 'gdnative_api.json', run_in_subprocess(gdnative_builders.build_gdnative_api_struct)) -gdn_env.add_source_files(env.modules_sources, [gensource]) +env_gdnative.add_source_files(env.modules_sources, [gensource]) env.use_ptrcall = True if ARGUMENTS.get('gdnative_wrapper', False): - gensource, = gdn_env.CommandNoCache('gdnative_wrapper_code.gen.cpp', 'gdnative_api.json', run_in_subprocess(gdnative_builders.build_gdnative_wrapper_code)) + gensource, = env_gdnative.CommandNoCache('gdnative_wrapper_code.gen.cpp', 'gdnative_api.json', run_in_subprocess(gdnative_builders.build_gdnative_wrapper_code)) gd_wrapper_env = env.Clone() gd_wrapper_env.Append(CPPPATH=['#modules/gdnative/include/']) diff --git a/modules/gdnative/arvr/SCsub b/modules/gdnative/arvr/SCsub index ecc5996108..20eaa99592 100644 --- a/modules/gdnative/arvr/SCsub +++ b/modules/gdnative/arvr/SCsub @@ -1,13 +1,6 @@ #!/usr/bin/env python -import os -import methods - Import('env') -Import('env_modules') - -env_arvr_gdnative = env_modules.Clone() - -env_arvr_gdnative.Append(CPPPATH=['#modules/gdnative/include/']) -env_arvr_gdnative.add_source_files(env.modules_sources, '*.cpp') +Import('env_gdnative') +env_gdnative.add_source_files(env.modules_sources, '*.cpp') diff --git a/modules/gdnative/nativescript/SCsub b/modules/gdnative/nativescript/SCsub index ee3b9c351d..5841ad5531 100644 --- a/modules/gdnative/nativescript/SCsub +++ b/modules/gdnative/nativescript/SCsub @@ -1,12 +1,10 @@ #!/usr/bin/env python Import('env') +Import('env_gdnative') -mod_env = env.Clone() -mod_env.add_source_files(env.modules_sources, "*.cpp") -mod_env.Append(CPPFLAGS=['-DGDAPI_BUILT_IN']) +env_gdnative.add_source_files(env.modules_sources, '*.cpp') +env_gdnative.Append(CPPFLAGS=['-DGDAPI_BUILT_IN']) if "platform" in env and env["platform"] in ["x11", "iphone"]: env.Append(LINKFLAGS=["-rdynamic"]) - -Export('mod_env') diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h index a96fe5c5e3..ade8ffd280 100644 --- a/modules/gdnative/nativescript/nativescript.h +++ b/modules/gdnative/nativescript/nativescript.h @@ -70,8 +70,6 @@ struct NativeScriptDesc { String documentation; }; - String documentation; - Map<StringName, Method> methods; OrderedHashMap<StringName, Property> properties; Map<StringName, Signal> signals_; // QtCreator doesn't like the name signals @@ -81,6 +79,8 @@ struct NativeScriptDesc { godot_instance_create_func create_func; godot_instance_destroy_func destroy_func; + String documentation; + const void *type_tag; bool is_tool; diff --git a/modules/gdnative/net/SCsub b/modules/gdnative/net/SCsub index 53f9271128..e915703935 100644 --- a/modules/gdnative/net/SCsub +++ b/modules/gdnative/net/SCsub @@ -1,12 +1,7 @@ #!/usr/bin/env python -import os -import methods - Import('env') -Import('env_modules') +Import('env_gdnative') -env_net_gdnative = env_modules.Clone() +env_gdnative.add_source_files(env.modules_sources, '*.cpp') -env_net_gdnative.Append(CPPPATH=['#modules/gdnative/include/']) -env_net_gdnative.add_source_files(env.modules_sources, '*.cpp') diff --git a/modules/gdnative/pluginscript/SCsub b/modules/gdnative/pluginscript/SCsub index 2031a4236b..20eaa99592 100644 --- a/modules/gdnative/pluginscript/SCsub +++ b/modules/gdnative/pluginscript/SCsub @@ -1,9 +1,6 @@ #!/usr/bin/env python Import('env') -Import('env_modules') +Import('env_gdnative') -env_pluginscript = env_modules.Clone() - -env_pluginscript.Append(CPPPATH=['#modules/gdnative/include/']) -env_pluginscript.add_source_files(env.modules_sources, '*.cpp') +env_gdnative.add_source_files(env.modules_sources, '*.cpp') diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp index 48c0fc8aef..62e87c3651 100644 --- a/modules/gdnative/register_types.cpp +++ b/modules/gdnative/register_types.cpp @@ -29,19 +29,19 @@ /*************************************************************************/ #include "register_types.h" + #include "gdnative/gdnative.h" #include "gdnative.h" -#include "core/io/resource_loader.h" -#include "core/io/resource_saver.h" - #include "arvr/register_types.h" #include "nativescript/register_types.h" #include "net/register_types.h" #include "pluginscript/register_types.h" #include "core/engine.h" +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" #include "core/os/os.h" #include "core/project_settings.h" @@ -148,7 +148,7 @@ protected: }; struct LibrarySymbol { - char *name; + const char *name; bool is_required; }; @@ -239,7 +239,7 @@ void GDNativeExportPlugin::_export_file(const String &p_path, const String &p_ty String additional_code = "extern void register_dynamic_symbol(char *name, void *address);\n" "extern void add_ios_init_callback(void (*cb)());\n"; String linker_flags = ""; - for (int i = 0; i < sizeof(expected_symbols) / sizeof(expected_symbols[0]); ++i) { + for (unsigned int i = 0; i < sizeof(expected_symbols) / sizeof(expected_symbols[0]); ++i) { String full_name = lib->get_symbol_prefix() + expected_symbols[i].name; String code = declare_pattern.replace("$name", full_name); code = code.replace("$weak", expected_symbols[i].is_required ? "" : " __attribute__((weak))"); @@ -255,7 +255,7 @@ void GDNativeExportPlugin::_export_file(const String &p_path, const String &p_ty additional_code += String("void $prefixinit() {\n").replace("$prefix", lib->get_symbol_prefix()); String register_pattern = " if (&$name) register_dynamic_symbol((char *)\"$name\", (void *)$name);\n"; - for (int i = 0; i < sizeof(expected_symbols) / sizeof(expected_symbols[0]); ++i) { + for (unsigned int i = 0; i < sizeof(expected_symbols) / sizeof(expected_symbols[0]); ++i) { String full_name = lib->get_symbol_prefix() + expected_symbols[i].name; additional_code += register_pattern.replace("$name", full_name); } diff --git a/modules/gdscript/SCsub b/modules/gdscript/SCsub index 73f09f1659..6904154953 100644 --- a/modules/gdscript/SCsub +++ b/modules/gdscript/SCsub @@ -9,5 +9,3 @@ env_gdscript.add_source_files(env.modules_sources, "*.cpp") if env['tools']: env_gdscript.add_source_files(env.modules_sources, "./editor/*.cpp") - -Export('env') diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index a1163b5d8d..c199667270 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -43,10 +43,6 @@ static bool _is_text_char(CharType c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; } -static bool _is_whitespace(CharType c) { - return c == '\t' || c == ' '; -} - static bool _is_char(CharType c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index b0d5422afe..48c1760662 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -1981,6 +1981,7 @@ String GDScriptWarning::get_message() const { CHECK_SYMBOLS(2); return "The '" + symbols[0] + "' keyword is deprecated and will be removed in a future release, please replace its uses by '" + symbols[1] + "'."; } break; + case WARNING_MAX: break; // Can't happen, but silences warning } ERR_EXPLAIN("Invalid GDScript warning code: " + get_name_from_code(code)); ERR_FAIL_V(String()); diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index d795500265..f344beba9f 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -147,7 +147,7 @@ public: const Map<StringName, Variant> &get_constants() const { return constants; } const Set<StringName> &get_members() const { return members; } const GDScriptDataType &get_member_type(const StringName &p_member) const { - ERR_FAIL_COND_V(!member_indices.has(p_member), GDScriptDataType()); + CRASH_COND(!member_indices.has(p_member)); return member_indices[p_member].data_type; } const Map<StringName, GDScriptFunction *> &get_member_functions() const { return member_functions; } @@ -301,8 +301,8 @@ struct GDScriptWarning { static Code get_code_from_name(const String &p_name); GDScriptWarning() : - line(-1), - code(WARNING_MAX) {} + code(WARNING_MAX), + line(-1) {} }; #endif // DEBUG_ENABLED diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index a9b641de50..ddd9e6b01c 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -33,12 +33,9 @@ #include "core/engine.h" #include "core/global_constants.h" #include "core/os/file_access.h" -#include "editor/editor_settings.h" #include "gdscript_compiler.h" #ifdef TOOLS_ENABLED -#include "core/engine.h" -#include "core/reference.h" #include "editor/editor_file_system.h" #include "editor/editor_settings.h" #endif @@ -54,12 +51,6 @@ void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const { p_delimiters->push_back("\"\"\" \"\"\""); } Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const { -#ifdef TOOLS_ENABLED - bool th = EDITOR_DEF("text_editor/completion/add_type_hints", false); -#else - bool th = false; -#endif - String _template = "extends %BASE%\n" "\n" "# Declare member variables here. Examples:\n" @@ -1113,6 +1104,7 @@ static bool _guess_expression_type(const GDScriptCompletionContext &p_context, c } break; } } break; + default: {} } // It may have found a null, but that's never useful @@ -3357,6 +3349,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol return OK; } } break; + default: {} } return ERR_CANT_RESOLVE; diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index abd08d13ff..b935861652 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -789,7 +789,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #ifdef DEBUG_ENABLED GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *()); GD_ERR_BREAK(!nc); - if (!src->get_type() != Variant::OBJECT && !src->get_type() != Variant::NIL) { + if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) { err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) + "' to a variable of type '" + nc->get_name() + "'."; OPCODE_BREAK; diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index ea1287374b..2fa5084d84 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -5079,7 +5079,7 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) { if (found) continue; if (p->constant_expressions.has(base)) { - if (!p->constant_expressions[base].expression->type == Node::TYPE_CONSTANT) { + if (p->constant_expressions[base].expression->type != Node::TYPE_CONSTANT) { _set_error("Could not resolve constant '" + base + "'.", p_class->line); return; } @@ -5219,6 +5219,8 @@ String GDScriptParser::DataType::to_string() const { } return class_type->name.operator String(); } break; + case UNRESOLVED: { + } break; } return "Unresolved"; @@ -5791,7 +5793,10 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data expr_native = base->base_type.native_type; expr_script = base->base_type.script_type; } - } + } break; + case DataType::BUILTIN: // Already handled above + case DataType::UNRESOLVED: // Not allowed, see above + break; } switch (p_container.kind) { @@ -5834,7 +5839,10 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data expr_class = expr_class->base_type.class_type; } return false; - } + } break; + case DataType::BUILTIN: // Already handled above + case DataType::UNRESOLVED: // Not allowed, see above + break; } return false; @@ -6228,6 +6236,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { case Variant::COLOR: { error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::STRING; } break; + default: {} } } if (error) { @@ -6345,6 +6354,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { } } } break; + default: {} } p_node->set_datatype(_resolve_type(node_type, p_node->line)); @@ -7825,7 +7835,7 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { // Figure out function name for warning String func_name = _find_function_name(op); if (func_name.empty()) { - func_name == "<undetected name>"; + func_name = "<undetected name>"; } _add_warning(GDScriptWarning::RETURN_VALUE_DISCARDED, op->line, func_name); } diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index cd68072499..8121fb7f85 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -88,6 +88,8 @@ public: case CLASS: { return class_type == other.class_type; } break; + case UNRESOLVED: { + } break; } return false; } @@ -553,7 +555,6 @@ private: CompletionType completion_type; StringName completion_cursor; - bool completion_static; Variant::Type completion_built_in_constant; Node *completion_node; ClassNode *completion_class; diff --git a/modules/gridmap/SCsub b/modules/gridmap/SCsub index 2ffe15cd33..62b8a0ff93 100644 --- a/modules/gridmap/SCsub +++ b/modules/gridmap/SCsub @@ -6,5 +6,3 @@ Import('env_modules') env_gridmap = env_modules.Clone() env_gridmap.add_source_files(env.modules_sources, "*.cpp") - -Export('env') diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index a480c4183e..a8fdf8cf1f 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -29,37 +29,19 @@ /*************************************************************************/ #include "grid_map.h" -#include "core/message_queue.h" -#include "scene/3d/light.h" -#include "scene/resources/surface_tool.h" -#include "servers/visual_server.h" #include "core/io/marshalls.h" -#include "core/os/os.h" +#include "core/message_queue.h" +#include "scene/3d/light.h" #include "scene/resources/mesh_library.h" +#include "scene/resources/surface_tool.h" #include "scene/scene_string_names.h" +#include "servers/visual_server.h" bool GridMap::_set(const StringName &p_name, const Variant &p_value) { String name = p_name; - /* } else if (name=="cells") { - PoolVector<int> cells = p_value; - int amount=cells.size(); - PoolVector<int>::Read r = cells.read(); - ERR_FAIL_COND_V(amount&1,false); // not even - cell_map.clear(); - for(int i=0;i<amount/3;i++) { - - - IndexKey ik; - ik.key=decode_uint64(&r[i*3]); - Cell cell; - cell.cell=uint32_t(r[i*+1]); - cell_map[ik]=cell; - - } - _recreate_octant_data();*/ if (name == "data") { Dictionary d = p_value; @@ -80,7 +62,9 @@ bool GridMap::_set(const StringName &p_name, const Variant &p_value) { cell_map[ik] = cell; } } + _recreate_octant_data(); + } else if (name == "baked_meshes") { clear_baked_meshes(); @@ -103,8 +87,9 @@ bool GridMap::_set(const StringName &p_name, const Variant &p_value) { _recreate_octant_data(); - } else + } else { return false; + } return true; } @@ -1081,8 +1066,6 @@ void GridMap::make_baked_meshes(bool p_gen_lightmap_uv, float p_lightmap_uv_texe } } - int ofs = 0; - for (Map<OctantKey, Map<Ref<Material>, Ref<SurfaceTool> > >::Element *E = surface_map.front(); E; E = E->next()) { Ref<ArrayMesh> mesh; diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index 5fdb6a5196..fae88042af 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -597,29 +597,31 @@ bool GridMapEditor::forward_spatial_input_event(Camera *p_camera, const Ref<Inpu if (mb->get_button_index() == BUTTON_LEFT) { if (input_action == INPUT_DUPLICATE) { - //paste _duplicate_paste(); input_action = INPUT_NONE; _update_duplicate_indicator(); } else if (mb->get_shift()) { input_action = INPUT_SELECT; - } else if (mb->get_command()) + } else if (mb->get_command()) { input_action = INPUT_COPY; - else { + } else { input_action = INPUT_PAINT; set_items.clear(); } - } else if (mb->get_button_index() == BUTTON_RIGHT) + } else if (mb->get_button_index() == BUTTON_RIGHT) { if (input_action == INPUT_DUPLICATE) { - input_action = INPUT_NONE; _update_duplicate_indicator(); } else if (mb->get_shift()) { input_action = INPUT_ERASE; set_items.clear(); - } else + } else { return false; + } + } else { + return false; + } return do_input_action(p_camera, Point2(mb->get_position().x, mb->get_position().y), true); } else { diff --git a/modules/jpg/SCsub b/modules/jpg/SCsub index e72dc6a1ca..d5f87905eb 100644 --- a/modules/jpg/SCsub +++ b/modules/jpg/SCsub @@ -13,8 +13,11 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_jpg.add_source_files(env.modules_sources, thirdparty_sources) env_jpg.Append(CPPPATH=[thirdparty_dir]) +env_thirdparty = env_jpg.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot's own source files env_jpg.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/mbedtls/SCsub b/modules/mbedtls/SCsub index d11d7a7ec7..0c6c703e16 100755 --- a/modules/mbedtls/SCsub +++ b/modules/mbedtls/SCsub @@ -91,8 +91,12 @@ if env['builtin_mbedtls']: thirdparty_dir = "#thirdparty/mbedtls/library/" thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_mbed_tls.add_source_files(env.modules_sources, thirdparty_sources) + env_mbed_tls.Prepend(CPPPATH=["#thirdparty/mbedtls/include/"]) + env_thirdparty = env_mbed_tls.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Module sources env_mbed_tls.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/mbedtls/stream_peer_mbed_tls.cpp b/modules/mbedtls/stream_peer_mbed_tls.cpp index e0cd67a810..5c81f32e9e 100755 --- a/modules/mbedtls/stream_peer_mbed_tls.cpp +++ b/modules/mbedtls/stream_peer_mbed_tls.cpp @@ -29,9 +29,11 @@ /*************************************************************************/ #include "stream_peer_mbed_tls.h" + #include "core/io/stream_peer_tcp.h" #include "core/os/file_access.h" -#include "mbedtls/platform_util.h" + +#include <mbedtls/platform_util.h> static void my_debug(void *ctx, int level, const char *file, int line, @@ -283,7 +285,7 @@ void StreamPeerMbedTLS::poll() { } Ref<StreamPeerTCP> tcp = base; - if (tcp.is_valid() && tcp->get_status() != STATUS_CONNECTED) { + if (tcp.is_valid() && tcp->get_status() != StreamPeerTCP::STATUS_CONNECTED) { disconnect_from_stream(); return; } @@ -310,7 +312,7 @@ void StreamPeerMbedTLS::disconnect_from_stream() { return; Ref<StreamPeerTCP> tcp = base; - if (tcp.is_valid() && tcp->get_status() == STATUS_CONNECTED) { + if (tcp.is_valid() && tcp->get_status() == StreamPeerTCP::STATUS_CONNECTED) { // We are still connected on the socket, try to send close notity. mbedtls_ssl_close_notify(&ssl); } diff --git a/modules/mbedtls/stream_peer_mbed_tls.h b/modules/mbedtls/stream_peer_mbed_tls.h index 0cf893eacf..abf87b79cc 100755 --- a/modules/mbedtls/stream_peer_mbed_tls.h +++ b/modules/mbedtls/stream_peer_mbed_tls.h @@ -33,12 +33,12 @@ #include "core/io/stream_peer_ssl.h" -#include "mbedtls/config.h" -#include "mbedtls/ctr_drbg.h" -#include "mbedtls/debug.h" -#include "mbedtls/entropy.h" -#include "mbedtls/net.h" -#include "mbedtls/ssl.h" +#include <mbedtls/config.h> +#include <mbedtls/ctr_drbg.h> +#include <mbedtls/debug.h> +#include <mbedtls/entropy.h> +#include <mbedtls/net.h> +#include <mbedtls/ssl.h> #include <stdio.h> #include <stdlib.h> diff --git a/modules/mobile_vr/SCsub b/modules/mobile_vr/SCsub index b4e2edcca1..e5725ceb6f 100644 --- a/modules/mobile_vr/SCsub +++ b/modules/mobile_vr/SCsub @@ -1,8 +1,5 @@ #!/usr/bin/env python -import os -import methods - Import('env') Import('env_modules') diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp index e2c630565f..2cabc7bd59 100644 --- a/modules/mobile_vr/mobile_vr_interface.cpp +++ b/modules/mobile_vr/mobile_vr_interface.cpp @@ -297,6 +297,7 @@ bool MobileVRInterface::initialize() { mag_current_min = Vector3(0, 0, 0); mag_current_max = Vector3(0, 0, 0); +#if !defined(SERVER_ENABLED) // build our shader if (lens_shader == NULL) { ///@TODO need to switch between GLES2 and GLES3 version, Reduz suggested moving this into our drivers and making this a core shader @@ -337,6 +338,7 @@ bool MobileVRInterface::initialize() { glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind } +#endif // reset our orientation orientation = Basis(); @@ -360,6 +362,7 @@ void MobileVRInterface::uninitialize() { arvr_server->clear_primary_interface_if(this); } +#if !defined(SERVER_ENABLED) // cleanup our shader and buffers if (lens_shader != NULL) { glDeleteVertexArrays(1, &half_screen_array); @@ -368,6 +371,7 @@ void MobileVRInterface::uninitialize() { delete lens_shader; lens_shader = NULL; } +#endif initialized = false; }; @@ -470,6 +474,7 @@ void MobileVRInterface::commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_t // get our render target RID eye_texture = VSG::storage->render_target_get_texture(p_render_target); uint32_t texid = VS::get_singleton()->texture_get_texid(eye_texture); +#if !defined(SERVER_ENABLED) glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texid); @@ -484,6 +489,7 @@ void MobileVRInterface::commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_t glBindVertexArray(half_screen_array); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glBindVertexArray(0); +#endif }; void MobileVRInterface::process() { diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h index cee0cca90e..63cad4c738 100644 --- a/modules/mobile_vr/mobile_vr_interface.h +++ b/modules/mobile_vr/mobile_vr_interface.h @@ -34,7 +34,9 @@ #include "servers/arvr/arvr_interface.h" #include "servers/arvr/arvr_positional_tracker.h" +#if !defined(SERVER_ENABLED) #include "shaders/lens_distorted.glsl.gen.h" +#endif /** @author Bastiaan Olij <mux213@gmail.com> @@ -58,9 +60,13 @@ private: float eye_height; uint64_t last_ticks; +#if !defined(SERVER_ENABLED) LensDistortedShaderGLES3 *lens_shader; GLuint half_screen_quad; GLuint half_screen_array; +#else + void *lens_shader; +#endif real_t intraocular_dist; real_t display_width; diff --git a/modules/mobile_vr/shaders/SCsub b/modules/mobile_vr/shaders/SCsub index cf53c9ebe0..97a3598598 100644 --- a/modules/mobile_vr/shaders/SCsub +++ b/modules/mobile_vr/shaders/SCsub @@ -4,4 +4,3 @@ Import('env') if 'GLES3_GLSL' in env['BUILDERS']: env.GLES3_GLSL('lens_distorted.glsl'); - diff --git a/modules/ogg/SCsub b/modules/ogg/SCsub index 5e559bd4db..765a9fc11a 100644 --- a/modules/ogg/SCsub +++ b/modules/ogg/SCsub @@ -14,8 +14,11 @@ if env['builtin_libogg']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_ogg.add_source_files(env.modules_sources, thirdparty_sources) env_ogg.Append(CPPPATH=[thirdparty_dir]) + env_thirdparty = env_ogg.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot source files env_ogg.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/opensimplex/SCsub b/modules/opensimplex/SCsub index be9c8b091f..4235f6a0b9 100644 --- a/modules/opensimplex/SCsub +++ b/modules/opensimplex/SCsub @@ -1,4 +1,22 @@ #!/usr/bin/env python Import('env') -env.add_source_files(env.modules_sources, ["register_types.cpp", "simplex_noise.cpp", "noise_texture.cpp", "#thirdparty/misc/open-simplex-noise.c"]) +Import('env_modules') + +env_opensimplex = env_modules.Clone() + +# Thirdparty source files +thirdparty_dir = "#thirdparty/misc/" +thirdparty_sources = [ + "open-simplex-noise.c", +] +thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + +env_opensimplex.Append(CPPPATH=[thirdparty_dir]) + +env_thirdparty = env_opensimplex.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + +# Godot's own source files +env_opensimplex.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/opensimplex/doc_classes/NoiseTexture.xml b/modules/opensimplex/doc_classes/NoiseTexture.xml index c7dc373f59..6af58e7a6b 100644 --- a/modules/opensimplex/doc_classes/NoiseTexture.xml +++ b/modules/opensimplex/doc_classes/NoiseTexture.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="NoiseTexture" inherits="Texture" category="Core" version="3.1"> <brief_description> - [SimplexNoise] filled texture. + [OpenSimplexNoise] filled texture. </brief_description> <description> - Uses a [SimplexNoise] to fill the texture data. You can specify the texture size but keep in mind that larger textures will take longer to generate and seamless noise only works with square sized textures. + Uses an [OpenSimplexNoise] to fill the texture data. You can specify the texture size but keep in mind that larger textures will take longer to generate and seamless noise only works with square sized textures. NoiseTexture can also generate normalmap textures. </description> <tutorials> @@ -35,8 +35,8 @@ <member name="as_normalmap" type="bool" setter="set_as_normalmap" getter="is_normalmap"> If true, the resulting texture contains a normal map created from the original noise interpreted as a bump map. </member> - <member name="noise" type="SimplexNoise" setter="set_noise" getter="get_noise"> - The [SimplexNoise] instance used to generate the noise. + <member name="noise" type="OpenSimplexNoise" setter="set_noise" getter="get_noise"> + The [OpenSimplexNoise] instance used to generate the noise. </member> <member name="seamless" type="bool" setter="set_seamless" getter="get_seamless"> Whether the texture can be tiled without visible seams or not. Seamless textures take longer to generate. diff --git a/modules/opensimplex/doc_classes/SimplexNoise.xml b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml index e5e0c15b3c..31f13f341c 100644 --- a/modules/opensimplex/doc_classes/SimplexNoise.xml +++ b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SimplexNoise" inherits="Resource" category="Core" version="3.1"> +<class name="OpenSimplexNoise" inherits="Resource" category="Core" version="3.1"> <brief_description> Noise generator based on Open Simplex. </brief_description> <description> - This resource allows you to configure and sample a fractal noise space. Here is a brief usage example that configures a SimplexNoise and gets samples at various positions and dimensions: + This resource allows you to configure and sample a fractal noise space. Here is a brief usage example that configures an OpenSimplexNoise and gets samples at various positions and dimensions: [codeblock] - var noise = SimplexNoise.new() + var noise = OpenSimplexNoise.new() # Configure noise.seed = randi() @@ -109,7 +109,7 @@ Difference in period between [member octaves]. </member> <member name="octaves" type="int" setter="set_octaves" getter="get_octaves"> - Number of Simplex noise layers that are sampled to get the fractal noise. + Number of OpenSimplex noise layers that are sampled to get the fractal noise. </member> <member name="period" type="float" setter="set_period" getter="get_period"> Period of the base octave. A lower period results in a higher-frequency noise (more value changes across the same distance). diff --git a/modules/opensimplex/noise_texture.cpp b/modules/opensimplex/noise_texture.cpp index b82b0b453f..6f2723e43b 100644 --- a/modules/opensimplex/noise_texture.cpp +++ b/modules/opensimplex/noise_texture.cpp @@ -43,7 +43,7 @@ NoiseTexture::NoiseTexture() { as_normalmap = false; flags = FLAGS_DEFAULT; - noise = Ref<SimplexNoise>(); + noise = Ref<OpenSimplexNoise>(); texture = VS::get_singleton()->texture_create(); @@ -76,7 +76,7 @@ void NoiseTexture::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "seamless"), "set_seamless", "get_seamless"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "as_normalmap"), "set_as_normalmap", "is_normalmap"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "SimplexNoise"), "set_noise", "get_noise"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "OpenSimplexNoise"), "set_noise", "get_noise"); } void NoiseTexture::_set_texture_data(const Ref<Image> &p_image) { @@ -159,7 +159,7 @@ void NoiseTexture::_update_texture() { } } -void NoiseTexture::set_noise(Ref<SimplexNoise> p_noise) { +void NoiseTexture::set_noise(Ref<OpenSimplexNoise> p_noise) { if (p_noise == noise) return; if (noise.is_valid()) { @@ -172,7 +172,7 @@ void NoiseTexture::set_noise(Ref<SimplexNoise> p_noise) { _queue_update(); } -Ref<SimplexNoise> NoiseTexture::get_noise() { +Ref<OpenSimplexNoise> NoiseTexture::get_noise() { return noise; } diff --git a/modules/opensimplex/noise_texture.h b/modules/opensimplex/noise_texture.h index 108e471fc1..78a02cda9f 100644 --- a/modules/opensimplex/noise_texture.h +++ b/modules/opensimplex/noise_texture.h @@ -31,7 +31,7 @@ #ifndef NOISE_TEXTURE_H #define NOISE_TEXTURE_H -#include "simplex_noise.h" +#include "open_simplex_noise.h" #include "core/image.h" #include "core/reference.h" @@ -54,7 +54,7 @@ private: RID texture; uint32_t flags; - Ref<SimplexNoise> noise; + Ref<OpenSimplexNoise> noise; Vector2i size; bool seamless; bool as_normalmap; @@ -71,8 +71,8 @@ protected: static void _bind_methods(); public: - void set_noise(Ref<SimplexNoise> p_noise); - Ref<SimplexNoise> get_noise(); + void set_noise(Ref<OpenSimplexNoise> p_noise); + Ref<OpenSimplexNoise> get_noise(); void set_width(int p_width); void set_height(int p_hieght); diff --git a/modules/opensimplex/simplex_noise.cpp b/modules/opensimplex/open_simplex_noise.cpp index e489b7f6f0..bfc2732ff4 100644 --- a/modules/opensimplex/simplex_noise.cpp +++ b/modules/opensimplex/open_simplex_noise.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* simplex_noise.cpp */ +/* open_simplex_noise.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,11 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "simplex_noise.h" +#include "open_simplex_noise.h" #include "core/core_string_names.h" -SimplexNoise::SimplexNoise() { +OpenSimplexNoise::OpenSimplexNoise() { seed = 0; persistence = 0.5; @@ -43,16 +43,16 @@ SimplexNoise::SimplexNoise() { _init_seeds(); } -SimplexNoise::~SimplexNoise() { +OpenSimplexNoise::~OpenSimplexNoise() { } -void SimplexNoise::_init_seeds() { +void OpenSimplexNoise::_init_seeds() { for (int i = 0; i < 6; ++i) { open_simplex_noise(seed + i * 2, &(contexts[i])); } } -void SimplexNoise::set_seed(int p_seed) { +void OpenSimplexNoise::set_seed(int p_seed) { if (seed == p_seed) return; @@ -64,36 +64,36 @@ void SimplexNoise::set_seed(int p_seed) { emit_changed(); } -int SimplexNoise::get_seed() { +int OpenSimplexNoise::get_seed() { return seed; } -void SimplexNoise::set_octaves(int p_octaves) { +void OpenSimplexNoise::set_octaves(int p_octaves) { if (p_octaves == octaves) return; octaves = CLAMP(p_octaves, 1, 6); emit_changed(); } -void SimplexNoise::set_period(float p_period) { +void OpenSimplexNoise::set_period(float p_period) { if (p_period == period) return; period = p_period; emit_changed(); } -void SimplexNoise::set_persistence(float p_persistence) { +void OpenSimplexNoise::set_persistence(float p_persistence) { if (p_persistence == persistence) return; persistence = p_persistence; emit_changed(); } -void SimplexNoise::set_lacunarity(float p_lacunarity) { +void OpenSimplexNoise::set_lacunarity(float p_lacunarity) { if (p_lacunarity == lacunarity) return; lacunarity = p_lacunarity; emit_changed(); } -Ref<Image> SimplexNoise::get_image(int p_width, int p_height) { +Ref<Image> OpenSimplexNoise::get_image(int p_width, int p_height) { PoolVector<uint8_t> data; data.resize(p_width * p_height * 4); @@ -116,7 +116,7 @@ Ref<Image> SimplexNoise::get_image(int p_width, int p_height) { return image; } -Ref<Image> SimplexNoise::get_seamless_image(int p_size) { +Ref<Image> OpenSimplexNoise::get_seamless_image(int p_size) { PoolVector<uint8_t> data; data.resize(p_size * p_size * 4); @@ -153,32 +153,32 @@ Ref<Image> SimplexNoise::get_seamless_image(int p_size) { return image; } -void SimplexNoise::_bind_methods() { +void OpenSimplexNoise::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_seed"), &SimplexNoise::get_seed); - ClassDB::bind_method(D_METHOD("set_seed", "seed"), &SimplexNoise::set_seed); + ClassDB::bind_method(D_METHOD("get_seed"), &OpenSimplexNoise::get_seed); + ClassDB::bind_method(D_METHOD("set_seed", "seed"), &OpenSimplexNoise::set_seed); - ClassDB::bind_method(D_METHOD("set_octaves", "octave_count"), &SimplexNoise::set_octaves); - ClassDB::bind_method(D_METHOD("get_octaves"), &SimplexNoise::get_octaves); + ClassDB::bind_method(D_METHOD("set_octaves", "octave_count"), &OpenSimplexNoise::set_octaves); + ClassDB::bind_method(D_METHOD("get_octaves"), &OpenSimplexNoise::get_octaves); - ClassDB::bind_method(D_METHOD("set_period", "period"), &SimplexNoise::set_period); - ClassDB::bind_method(D_METHOD("get_period"), &SimplexNoise::get_period); + ClassDB::bind_method(D_METHOD("set_period", "period"), &OpenSimplexNoise::set_period); + ClassDB::bind_method(D_METHOD("get_period"), &OpenSimplexNoise::get_period); - ClassDB::bind_method(D_METHOD("set_persistence", "persistence"), &SimplexNoise::set_persistence); - ClassDB::bind_method(D_METHOD("get_persistence"), &SimplexNoise::get_persistence); + ClassDB::bind_method(D_METHOD("set_persistence", "persistence"), &OpenSimplexNoise::set_persistence); + ClassDB::bind_method(D_METHOD("get_persistence"), &OpenSimplexNoise::get_persistence); - ClassDB::bind_method(D_METHOD("set_lacunarity", "lacunarity"), &SimplexNoise::set_lacunarity); - ClassDB::bind_method(D_METHOD("get_lacunarity"), &SimplexNoise::get_lacunarity); + ClassDB::bind_method(D_METHOD("set_lacunarity", "lacunarity"), &OpenSimplexNoise::set_lacunarity); + ClassDB::bind_method(D_METHOD("get_lacunarity"), &OpenSimplexNoise::get_lacunarity); - ClassDB::bind_method(D_METHOD("get_image", "width", "height"), &SimplexNoise::get_image); - ClassDB::bind_method(D_METHOD("get_seamless_image", "size"), &SimplexNoise::get_seamless_image); + ClassDB::bind_method(D_METHOD("get_image", "width", "height"), &OpenSimplexNoise::get_image); + ClassDB::bind_method(D_METHOD("get_seamless_image", "size"), &OpenSimplexNoise::get_seamless_image); - ClassDB::bind_method(D_METHOD("get_noise_2d", "x", "y"), &SimplexNoise::get_noise_2d); - ClassDB::bind_method(D_METHOD("get_noise_3d", "x", "y", "z"), &SimplexNoise::get_noise_3d); - ClassDB::bind_method(D_METHOD("get_noise_4d", "x", "y", "z", "w"), &SimplexNoise::get_noise_4d); + ClassDB::bind_method(D_METHOD("get_noise_2d", "x", "y"), &OpenSimplexNoise::get_noise_2d); + ClassDB::bind_method(D_METHOD("get_noise_3d", "x", "y", "z"), &OpenSimplexNoise::get_noise_3d); + ClassDB::bind_method(D_METHOD("get_noise_4d", "x", "y", "z", "w"), &OpenSimplexNoise::get_noise_4d); - ClassDB::bind_method(D_METHOD("get_noise_2dv", "pos"), &SimplexNoise::get_noise_2dv); - ClassDB::bind_method(D_METHOD("get_noise_3dv", "pos"), &SimplexNoise::get_noise_3dv); + ClassDB::bind_method(D_METHOD("get_noise_2dv", "pos"), &OpenSimplexNoise::get_noise_2dv); + ClassDB::bind_method(D_METHOD("get_noise_3dv", "pos"), &OpenSimplexNoise::get_noise_3dv); ADD_PROPERTY(PropertyInfo(Variant::INT, "seed"), "set_seed", "get_seed"); ADD_PROPERTY(PropertyInfo(Variant::INT, "octaves", PROPERTY_HINT_RANGE, "1,6,1"), "set_octaves", "get_octaves"); @@ -187,7 +187,7 @@ void SimplexNoise::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "lacunarity", PROPERTY_HINT_RANGE, "0.1,4.0,0.01"), "set_lacunarity", "get_lacunarity"); } -float SimplexNoise::get_noise_2d(float x, float y) { +float OpenSimplexNoise::get_noise_2d(float x, float y) { x /= period; y /= period; @@ -196,7 +196,7 @@ float SimplexNoise::get_noise_2d(float x, float y) { float max = 1.0; float sum = _get_octave_noise_2d(0, x, y); - unsigned int i = 0; + int i = 0; while (++i < octaves) { x *= lacunarity; y *= lacunarity; @@ -208,7 +208,7 @@ float SimplexNoise::get_noise_2d(float x, float y) { return sum / max; } -float SimplexNoise::get_noise_3d(float x, float y, float z) { +float OpenSimplexNoise::get_noise_3d(float x, float y, float z) { x /= period; y /= period; @@ -218,7 +218,7 @@ float SimplexNoise::get_noise_3d(float x, float y, float z) { float max = 1.0; float sum = _get_octave_noise_3d(0, x, y, z); - unsigned int i = 0; + int i = 0; while (++i < octaves) { x *= lacunarity; y *= lacunarity; @@ -231,7 +231,7 @@ float SimplexNoise::get_noise_3d(float x, float y, float z) { return sum / max; } -float SimplexNoise::get_noise_4d(float x, float y, float z, float w) { +float OpenSimplexNoise::get_noise_4d(float x, float y, float z, float w) { x /= period; y /= period; @@ -242,7 +242,7 @@ float SimplexNoise::get_noise_4d(float x, float y, float z, float w) { float max = 1.0; float sum = _get_octave_noise_4d(0, x, y, z, w); - unsigned int i = 0; + int i = 0; while (++i < octaves) { x *= lacunarity; y *= lacunarity; diff --git a/modules/opensimplex/simplex_noise.h b/modules/opensimplex/open_simplex_noise.h index 9a48dbf809..a9bee266e8 100644 --- a/modules/opensimplex/simplex_noise.h +++ b/modules/opensimplex/open_simplex_noise.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* simplex_noise.h */ +/* open_simplex_noise.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef SIMPLEX_NOISE_H -#define SIMPLEX_NOISE_H +#ifndef OPEN_SIMPLEX_NOISE_H +#define OPEN_SIMPLEX_NOISE_H #include "core/image.h" #include "core/reference.h" @@ -37,9 +37,9 @@ #include "thirdparty/misc/open-simplex-noise.h" -class SimplexNoise : public Resource { - GDCLASS(SimplexNoise, Resource) - OBJ_SAVE_TYPE(SimplexNoise); +class OpenSimplexNoise : public Resource { + GDCLASS(OpenSimplexNoise, Resource) + OBJ_SAVE_TYPE(OpenSimplexNoise); osn_context contexts[6]; @@ -50,8 +50,8 @@ class SimplexNoise : public Resource { float lacunarity; // Controls period change across octaves. 2 is usually a good value to address all detail levels. public: - SimplexNoise(); - ~SimplexNoise(); + OpenSimplexNoise(); + ~OpenSimplexNoise(); void _init_seeds(); @@ -90,4 +90,4 @@ protected: static void _bind_methods(); }; -#endif // OPENSIMPLEX_NOISE_H +#endif // OPEN_SIMPLEX_NOISE_H diff --git a/modules/opensimplex/register_types.cpp b/modules/opensimplex/register_types.cpp index 9e4af99651..d1c77da257 100644 --- a/modules/opensimplex/register_types.cpp +++ b/modules/opensimplex/register_types.cpp @@ -30,11 +30,11 @@ #include "register_types.h" #include "noise_texture.h" -#include "simplex_noise.h" +#include "open_simplex_noise.h" void register_opensimplex_types() { - ClassDB::register_class<SimplexNoise>(); + ClassDB::register_class<OpenSimplexNoise>(); ClassDB::register_class<NoiseTexture>(); } diff --git a/modules/opus/SCsub b/modules/opus/SCsub index 6f643ef08c..508aec7057 100644 --- a/modules/opus/SCsub +++ b/modules/opus/SCsub @@ -3,7 +3,6 @@ Import('env') Import('env_modules') - stub = True env_opus = env_modules.Clone() @@ -198,7 +197,10 @@ if env['builtin_opus']: thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources + opus_sources_silk] - env_opus.add_source_files(env.modules_sources, thirdparty_sources) + # also requires libogg + if env['builtin_libogg']: + env_opus.Append(CPPPATH=["#thirdparty/libogg"]) + env_opus.Append(CFLAGS=["-DHAVE_CONFIG_H"]) thirdparty_include_paths = [ @@ -211,9 +213,9 @@ if env['builtin_opus']: ] env_opus.Append(CPPPATH=[thirdparty_dir + "/" + dir for dir in thirdparty_include_paths]) - # also requires libogg - if env['builtin_libogg']: - env_opus.Append(CPPPATH=["#thirdparty/libogg"]) + env_thirdparty = env_opus.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) if not stub: # Module files diff --git a/modules/pvr/SCsub b/modules/pvr/SCsub index ddca7a794e..2e4a792a36 100644 --- a/modules/pvr/SCsub +++ b/modules/pvr/SCsub @@ -17,8 +17,11 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_pvr.add_source_files(env.modules_sources, thirdparty_sources) env_pvr.Append(CPPPATH=[thirdparty_dir]) +env_thirdparty = env_pvr.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot source files env_pvr.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/recast/SCsub b/modules/recast/SCsub index f56be72b24..4a06653968 100644 --- a/modules/recast/SCsub +++ b/modules/recast/SCsub @@ -23,10 +23,11 @@ if env['builtin_recast']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_recast.add_source_files(env.modules_sources, thirdparty_sources) env_recast.Append(CPPPATH=[thirdparty_dir + "/Include"]) + env_thirdparty = env_recast.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot source files env_recast.add_source_files(env.modules_sources, "*.cpp") - -Export('env') diff --git a/modules/regex/SCsub b/modules/regex/SCsub index 4b8d5e9283..99c25add45 100644 --- a/modules/regex/SCsub +++ b/modules/regex/SCsub @@ -4,15 +4,16 @@ Import('env') Import('env_modules') env_regex = env_modules.Clone() -env_regex.Append(CPPFLAGS=["-DPCRE2_CODE_UNIT_WIDTH=0"]) -env_regex.add_source_files(env.modules_sources, "*.cpp") if env['builtin_pcre2']: jit_blacklist = ['javascript', 'uwp'] + thirdparty_dir = '#thirdparty/pcre2/src/' thirdparty_flags = ['-DPCRE2_STATIC', '-DHAVE_CONFIG_H'] + if 'platform' in env and env['platform'] not in jit_blacklist: thirdparty_flags.append('-DSUPPORT_JIT') + thirdparty_sources = [ "pcre2_auto_possess.c", "pcre2_chartables.c", @@ -42,15 +43,21 @@ if env['builtin_pcre2']: "pcre2_valid_utf.c", "pcre2_xclass.c", ] + thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + env_regex.Append(CPPPATH=[thirdparty_dir]) env_regex.Append(CPPFLAGS=thirdparty_flags) + def pcre2_builtin(width): - env_pcre2 = env_modules.Clone() + env_pcre2 = env_regex.Clone() + env_pcre2.disable_warnings() env_pcre2["OBJSUFFIX"] = "_" + width + env_pcre2["OBJSUFFIX"] - env_pcre2.Append(CPPPATH=[thirdparty_dir]) env_pcre2.add_source_files(env.modules_sources, thirdparty_sources) - env_pcre2.Append(CPPFLAGS=thirdparty_flags) env_pcre2.Append(CPPFLAGS=["-DPCRE2_CODE_UNIT_WIDTH=" + width]) + pcre2_builtin("16") pcre2_builtin("32") + +env_regex.Append(CPPFLAGS=["-DPCRE2_CODE_UNIT_WIDTH=0"]) +env_regex.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/squish/SCsub b/modules/squish/SCsub index 127f22d798..3be85a1efa 100644 --- a/modules/squish/SCsub +++ b/modules/squish/SCsub @@ -22,8 +22,11 @@ if env['builtin_squish']: thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_squish.add_source_files(env.modules_sources, thirdparty_sources) env_squish.Append(CPPPATH=[thirdparty_dir]) + env_thirdparty = env_squish.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot source files env_squish.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/stb_vorbis/SCsub b/modules/stb_vorbis/SCsub index 897d05961c..d14939a3b1 100644 --- a/modules/stb_vorbis/SCsub +++ b/modules/stb_vorbis/SCsub @@ -3,8 +3,14 @@ Import('env') Import('env_modules') +env_stb_vorbis = env_modules.Clone() + # Thirdparty source files +thirdparty_sources = ["#thirdparty/misc/stb_vorbis.c"] -env_stb_vorbis = env_modules.Clone() +env_thirdparty = env_stb_vorbis.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) +# Godot's own source files env_stb_vorbis.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp index 57b6b5343e..5dbe0b4b00 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp @@ -32,11 +32,6 @@ #include "core/os/file_access.h" -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" -#include "thirdparty/misc/stb_vorbis.c" -#pragma GCC diagnostic pop - void AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) { ERR_FAIL_COND(!active); diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.h b/modules/stb_vorbis/audio_stream_ogg_vorbis.h index 71a957a6af..8b42111847 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.h +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.h @@ -34,12 +34,7 @@ #include "core/io/resource_loader.h" #include "servers/audio/audio_stream.h" -#define STB_VORBIS_HEADER_ONLY -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" -#include "thirdparty/misc/stb_vorbis.c" -#pragma GCC diagnostic pop -#undef STB_VORBIS_HEADER_ONLY +#include "thirdparty/misc/stb_vorbis.h" class AudioStreamOGGVorbis; diff --git a/modules/svg/SCsub b/modules/svg/SCsub index a41e0703bd..22f0b1e3eb 100644 --- a/modules/svg/SCsub +++ b/modules/svg/SCsub @@ -1,6 +1,9 @@ #!/usr/bin/env python Import('env') +Import('env_modules') + +env_svg = env_modules.Clone() # Thirdparty source files thirdparty_dir = "#thirdparty/nanosvg/" @@ -9,11 +12,15 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env.add_source_files(env.modules_sources, thirdparty_sources) +env_svg.Append(CPPPATH=[thirdparty_dir]) +# FIXME: Needed in editor/editor_themes.cpp for now, but ideally there +# shouldn't be a dependency on modules/ and its own 3rd party deps. env.Append(CPPPATH=[thirdparty_dir]) env.Append(CCFLAGS=["-DSVG_ENABLED"]) -# Godot's own source files -env.add_source_files(env.modules_sources, "*.cpp") +env_thirdparty = env_svg.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) -Export('env') +# Godot's own source files +env_svg.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/thekla_unwrap/SCsub b/modules/thekla_unwrap/SCsub index d23ba10d4c..c47c760d5f 100644 --- a/modules/thekla_unwrap/SCsub +++ b/modules/thekla_unwrap/SCsub @@ -54,11 +54,10 @@ if env['builtin_thekla_atlas']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_thekla_unwrap.add_source_files(env.modules_sources, thirdparty_sources) - env_thekla_unwrap.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "/poshlib", thirdparty_dir + "/nvcore", thirdparty_dir + "/nvmesh"]) + env_thekla_unwrap.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "poshlib", thirdparty_dir + "nvcore", thirdparty_dir + "nvmesh"]) # upstream uses c++11 - if (not env_thekla_unwrap.msvc): + if (not env.msvc): env_thekla_unwrap.Append(CXXFLAGS="-std=c++11") if env["platform"] == 'x11': @@ -78,5 +77,9 @@ if env['builtin_thekla_atlas']: env_thekla_unwrap.Append(CCFLAGS=["-DNV_OS_MINGW", "-DNV_CC_GNUC", "-DPOSH_COMPILER_GCC", "-U__STRICT_ANSI__"]) env.Append(LIBS=["dbghelp"]) + env_thirdparty = env_thekla_unwrap.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot source files env_thekla_unwrap.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/theora/SCsub b/modules/theora/SCsub index 9015c2c354..98c4274a7e 100644 --- a/modules/theora/SCsub +++ b/modules/theora/SCsub @@ -70,7 +70,6 @@ if env['builtin_libtheora']: thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_theora.add_source_files(env.modules_sources, thirdparty_sources) env_theora.Append(CPPPATH=[thirdparty_dir]) # also requires libogg and libvorbis @@ -79,5 +78,9 @@ if env['builtin_libtheora']: if env['builtin_libvorbis']: env_theora.Append(CPPPATH=["#thirdparty/libvorbis"]) + env_thirdparty = env_theora.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot source files env_theora.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index 44052d473f..d72d74cf79 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -332,8 +332,8 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) { int w; int h; - w = (ti.pic_x + ti.frame_width + 1 & ~1) - (ti.pic_x & ~1); - h = (ti.pic_y + ti.frame_height + 1 & ~1) - (ti.pic_y & ~1); + w = ((ti.pic_x + ti.frame_width + 1) & ~1) - (ti.pic_x & ~1); + h = ((ti.pic_y + ti.frame_height + 1) & ~1) - (ti.pic_y & ~1); size.x = w; size.y = h; @@ -439,7 +439,7 @@ void VideoStreamPlaybackTheora::update(float p_delta) { } } - int tr = vorbis_synthesis_read(&vd, ret - to_read); + vorbis_synthesis_read(&vd, ret - to_read); audio_frames_wrote += ret - to_read; diff --git a/modules/tinyexr/SCsub b/modules/tinyexr/SCsub index 38fd00cc65..3e7bda2bca 100644 --- a/modules/tinyexr/SCsub +++ b/modules/tinyexr/SCsub @@ -13,8 +13,11 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_tinyexr.add_source_files(env.modules_sources, thirdparty_sources) env_tinyexr.Append(CPPPATH=[thirdparty_dir]) +env_thirdparty = env_tinyexr.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot's own source files env_tinyexr.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/upnp/SCsub b/modules/upnp/SCsub index cde231867f..2b15f7aee2 100644 --- a/modules/upnp/SCsub +++ b/modules/upnp/SCsub @@ -25,8 +25,12 @@ if env['builtin_miniupnpc']: ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_upnp.add_source_files(env.modules_sources, thirdparty_sources) env_upnp.Append(CPPPATH=[thirdparty_dir]) env_upnp.Append(CPPFLAGS=["-DMINIUPNP_STATICLIB"]) + env_thirdparty = env_upnp.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + +# Godot source files env_upnp.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/visual_script/SCsub b/modules/visual_script/SCsub index 96ee911ba0..3c3d2caa57 100644 --- a/modules/visual_script/SCsub +++ b/modules/visual_script/SCsub @@ -6,5 +6,3 @@ Import('env_modules') env_vs = env_modules.Clone() env_vs.add_source_files(env.modules_sources, "*.cpp") - -Export('env') diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 1027c74f34..79f71535ad 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -2518,8 +2518,6 @@ void VisualScriptEditor::_port_action_menu(int p_option) { } ofs /= EDSCALE; - bool seq_connect = false; - Set<int> vn; switch (p_option) { @@ -2552,7 +2550,6 @@ void VisualScriptEditor::_port_action_menu(int p_option) { } } break; case CREATE_ACTION: { - seq_connect = true; VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); PropertyInfo property_info = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output); if (tg.type == Variant::OBJECT) { @@ -2603,7 +2600,6 @@ void VisualScriptEditor::connect_data(Ref<VisualScriptNode> vnode_old, Ref<Visua if (port >= value_count) { port = 0; } - int count = vnode_old->get_output_value_port_count() + vnode_old->get_output_sequence_port_count(); undo_redo->add_do_method(script.ptr(), "data_connect", edited_func, port_action_node, port, new_id, 0); undo_redo->add_undo_method(script.ptr(), "data_disconnect", edited_func, port_action_node, port, new_id, 0); undo_redo->commit_action(); @@ -2657,7 +2653,6 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri Ref<VisualScriptNode> vnode; - seq_connect = false; if (p_category == String("method")) { Ref<VisualScriptFunctionCall> n; @@ -2683,38 +2678,32 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri Ref<VisualScriptCondition> n; n.instance(); vnode = n; - seq_connect = true; } if (p_text == "VisualScriptSwitch") { Ref<VisualScriptSwitch> n; n.instance(); vnode = n; - seq_connect = true; } else if (p_text == "VisualScriptSequence") { Ref<VisualScriptSequence> n; n.instance(); vnode = n; - seq_connect = true; } else if (p_text == "VisualScriptIterator") { Ref<VisualScriptIterator> n; n.instance(); vnode = n; - seq_connect = true; } else if (p_text == "VisualScriptWhile") { Ref<VisualScriptWhile> n; n.instance(); vnode = n; - seq_connect = true; } else if (p_text == "VisualScriptReturn") { Ref<VisualScriptReturn> n; n.instance(); vnode = n; - seq_connect = true; } } @@ -2826,7 +2815,6 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri } void VisualScriptEditor::connect_seq(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode_new, int new_id) { - int seq_count = vnode_old->get_output_sequence_port_count(); VisualScriptOperator *vnode_operator = Object::cast_to<VisualScriptOperator>(vnode_new.ptr()); if (vnode_operator != NULL && vnode_operator->has_input_sequence_port() == false) { return; @@ -2841,7 +2829,7 @@ void VisualScriptEditor::connect_seq(Ref<VisualScriptNode> vnode_old, Ref<Visual if (vnode_new->has_input_sequence_port() == false) { return; } - VisualScriptFunction *vnode_function = Object::cast_to<VisualScriptFunction>(vnode_old.ptr()); + undo_redo->create_action(TTR("Connect Node Sequence")); int pass_port = -vnode_old->get_output_sequence_port_count() + 1; int return_port = port_action_output - 1; @@ -3367,11 +3355,6 @@ void VisualScriptEditor::_member_option(int p_option) { undo_redo->add_undo_method(script.ptr(), "data_connect", name, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); } - /* - for(int i=0;i<script->function_get_argument_count(name);i++) { - undo_redo->add_undo_method(script.ptr(),"function_add_argument",name,script->function_get_argument_name(name,i),script->function_get_argument_type(name,i)); - } - */ undo_redo->add_do_method(this, "_update_members"); undo_redo->add_undo_method(this, "_update_members"); undo_redo->add_do_method(this, "_update_graph"); diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index b0bf971630..fb90e346a4 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -37,6 +37,7 @@ #include "scene/gui/graph_edit.h" #include "visual_script.h" #include "visual_script_property_selector.h" + class VisualScriptEditorSignalEdit; class VisualScriptEditorVariableEdit; @@ -159,8 +160,6 @@ class VisualScriptEditor : public ScriptEditorBase { MemberType member_type; String member_name; - bool seq_connect = false; - PortAction port_action; int port_action_node; int port_action_output; diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/visual_script_property_selector.cpp index 9942d5baa6..cd29df9855 100644 --- a/modules/visual_script/visual_script_property_selector.cpp +++ b/modules/visual_script/visual_script_property_selector.cpp @@ -190,15 +190,14 @@ void VisualScriptPropertySelector::_update_search() { if (type_filter.size() && type_filter.find(E->get().type) == -1) continue; + // capitalize() also converts underscore to space, we'll match again both possible styles String get_text_raw = String(vformat(TTR("Get %s"), E->get().name)); String get_text = get_text_raw.capitalize(); - String set_text_raw = String(vformat(TTR("Set %s"), E->get().name)); String set_text = set_text_raw.capitalize(); String input = search_box->get_text().capitalize(); - if (input == String() || - get_text_raw.findn(input) != -1 || - get_text.findn(input) != -1) { + + if (input == String() || get_text_raw.findn(input) != -1 || get_text.findn(input) != -1) { TreeItem *item = search_options->create_item(category ? category : root); item->set_text(0, get_text); item->set_metadata(0, E->get().name); @@ -211,9 +210,7 @@ void VisualScriptPropertySelector::_update_search() { item->set_metadata(2, connecting); } - if (input == String() || - set_text_raw.findn(input) != -1 && - set_text.findn(input) != -1) { + if (input == String() || set_text_raw.findn(input) != -1 || set_text.findn(input) != -1) { TreeItem *item = search_options->create_item(category ? category : root); item->set_text(0, set_text); item->set_metadata(0, E->get().name); @@ -389,8 +386,8 @@ void VisualScriptPropertySelector::get_visual_node_names(const String &root_filt } Vector<String> path = E->get().split("/"); bool is_filter = false; - for (Set<String>::Element *E = filter.front(); E; E = E->next()) { - if (path.size() >= 2 && path[1].findn(E->get()) != -1) { + for (Set<String>::Element *F = filter.front(); F; F = F->next()) { + if (path.size() >= 2 && path[1].findn(F->get()) != -1) { is_filter = true; break; } @@ -578,6 +575,7 @@ void VisualScriptPropertySelector::select_from_base_type(const String &p_base, c type = Variant::NIL; script = 0; properties = true; + visual_script_generic = false; instance = NULL; virtuals_only = p_virtuals_only; @@ -598,6 +596,7 @@ void VisualScriptPropertySelector::select_from_script(const Ref<Script> &p_scrip type = Variant::NIL; script = p_script->get_instance_id(); properties = true; + visual_script_generic = false; instance = NULL; virtuals_only = false; @@ -617,6 +616,7 @@ void VisualScriptPropertySelector::select_from_basic_type(Variant::Type p_type, type = p_type; script = 0; properties = true; + visual_script_generic = false; instance = NULL; virtuals_only = false; @@ -635,6 +635,7 @@ void VisualScriptPropertySelector::select_from_action(const String &p_type, cons type = Variant::NIL; script = 0; properties = false; + visual_script_generic = false; instance = NULL; virtuals_only = false; @@ -653,6 +654,7 @@ void VisualScriptPropertySelector::select_from_instance(Object *p_instance, cons type = Variant::NIL; script = 0; properties = true; + visual_script_generic = false; instance = p_instance; virtuals_only = false; @@ -721,6 +723,7 @@ VisualScriptPropertySelector::VisualScriptPropertySelector() { search_options->set_hide_root(true); search_options->set_hide_folding(true); virtuals_only = false; + seq_connect = false; help_bit = memnew(EditorHelpBit); vbc->add_margin_child(TTR("Description:"), help_bit); help_bit->connect("request_hide", this, "_closed"); diff --git a/modules/visual_script/visual_script_property_selector.h b/modules/visual_script/visual_script_property_selector.h index 917ef9ae6d..f974ee3355 100644 --- a/modules/visual_script/visual_script_property_selector.h +++ b/modules/visual_script/visual_script_property_selector.h @@ -63,8 +63,7 @@ class VisualScriptPropertySelector : public ConfirmationDialog { ObjectID script; Object *instance; bool virtuals_only; - - bool seq_connect = false; + bool seq_connect; void _item_selected(); diff --git a/modules/vorbis/SCsub b/modules/vorbis/SCsub index 55a112585b..19587563ab 100644 --- a/modules/vorbis/SCsub +++ b/modules/vorbis/SCsub @@ -40,13 +40,16 @@ if env['builtin_libvorbis']: thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - env_vorbis.add_source_files(env.modules_sources, thirdparty_sources) env_vorbis.Append(CPPPATH=[thirdparty_dir]) # also requires libogg if env['builtin_libogg']: env_vorbis.Append(CPPPATH=["#thirdparty/libogg"]) + env_thirdparty = env_vorbis.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + if not stub: # Module files env_vorbis.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/webm/SCsub b/modules/webm/SCsub index 33561da098..cb35b926ab 100644 --- a/modules/webm/SCsub +++ b/modules/webm/SCsub @@ -6,17 +6,16 @@ Import('env_modules') env_webm = env_modules.Clone() # Thirdparty source files -thirdparty_libsimplewebm_dir = "#thirdparty/libsimplewebm/" -thirdparty_libsimplewebm_sources = [ +thirdparty_dir = "#thirdparty/libsimplewebm/" +thirdparty_sources = [ "libwebm/mkvparser/mkvparser.cc", "OpusVorbisDecoder.cpp", "VPXDecoder.cpp", "WebMDemuxer.cpp", ] -thirdparty_libsimplewebm_sources = [thirdparty_libsimplewebm_dir + file for file in thirdparty_libsimplewebm_sources] +thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_webm.add_source_files(env.modules_sources, thirdparty_libsimplewebm_sources) -env_webm.Append(CPPPATH=[thirdparty_libsimplewebm_dir, thirdparty_libsimplewebm_dir + "libwebm/"]) +env_webm.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "libwebm/"]) # upstream uses c++11 if (not env_webm.msvc): @@ -31,8 +30,12 @@ if env['builtin_opus']: env_webm.Append(CPPPATH=["#thirdparty/opus"]) if env['builtin_libvpx']: - Export('env_webm') + env_webm.Append(CPPPATH=["#thirdparty/libvpx"]) SConscript("libvpx/SCsub") +env_thirdparty = env_webm.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot source files env_webm.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/webm/libvpx/SCsub b/modules/webm/libvpx/SCsub index 711000bd9f..98e38b9027 100644 --- a/modules/webm/libvpx/SCsub +++ b/modules/webm/libvpx/SCsub @@ -1,5 +1,10 @@ #!/usr/bin/env python +Import('env') +Import('env_modules') + +# Thirdparty sources + libvpx_dir = "#thirdparty/libvpx/" libvpx_sources = [ @@ -249,12 +254,8 @@ libvpx_sources_arm_neon_armasm_ms = [libvpx_dir + file for file in libvpx_source libvpx_sources_arm_neon_gas_apple = [libvpx_dir + file for file in libvpx_sources_arm_neon_gas_apple] -Import('env') -Import('env_webm') - -env_webm.Append(CPPPATH=[libvpx_dir]) - -env_libvpx = env.Clone() +env_libvpx = env_modules.Clone() +env_libvpx.disable_warnings() env_libvpx.Append(CPPPATH=[libvpx_dir]) webm_multithread = env["platform"] != 'javascript' diff --git a/modules/webp/SCsub b/modules/webp/SCsub index 21ae0ce7c2..8a4307fbe1 100644 --- a/modules/webp/SCsub +++ b/modules/webp/SCsub @@ -127,8 +127,11 @@ if env['builtin_libwebp']: ] thirdparty_sources = [thirdparty_dir + "src/" + file for file in thirdparty_sources] - env_webp.add_source_files(env.modules_sources, thirdparty_sources) env_webp.Append(CPPPATH=[thirdparty_dir, thirdparty_dir + "src/"]) + env_thirdparty = env_webp.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot source files env_webp.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub index c0985b3245..b67a836fe8 100644 --- a/modules/websocket/SCsub +++ b/modules/websocket/SCsub @@ -7,7 +7,7 @@ Import('env_modules') env_lws = env_modules.Clone() -if env['builtin_libwebsockets']: +if env['builtin_libwebsockets'] and not env["platform"] == "javascript": # already builtin for javascript thirdparty_dir = "#thirdparty/libwebsockets/" helper_dir = "win32helpers/" thirdparty_sources = [ @@ -61,34 +61,33 @@ if env['builtin_libwebsockets']: "tls/mbedtls/mbedtls-server.c" ] - if env_lws["platform"] == "android": # Builtin getifaddrs + if env["platform"] == "android": # Builtin getifaddrs thirdparty_sources += ["misc/getifaddrs.c"] - if env_lws["platform"] == "windows" or env_lws["platform"] == "uwp": # Winsock + if env["platform"] == "windows" or env["platform"] == "uwp": # Winsock thirdparty_sources += ["plat/lws-plat-win.c", helper_dir + "getopt.c", helper_dir + "getopt_long.c", helper_dir + "gettimeofday.c"] else: # Unix socket thirdparty_sources += ["plat/lws-plat-unix.c"] - thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] - if env_lws["platform"] == "javascript": # No need to add third party libraries at all - pass - else: - env_lws.add_source_files(env.modules_sources, thirdparty_sources) - env_lws.Append(CPPPATH=[thirdparty_dir]) + env_lws.Append(CPPPATH=[thirdparty_dir]) + + if env['builtin_mbedtls']: + mbedtls_includes = "#thirdparty/mbedtls/include" + env_lws.Prepend(CPPPATH=[mbedtls_includes]) - wrapper_includes = ["#thirdparty/libwebsockets/tls/mbedtls/wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]] - env_lws.Prepend(CPPPATH=wrapper_includes) + wrapper_includes = ["#thirdparty/libwebsockets/tls/mbedtls/wrapper/include/" + inc for inc in ["internal", "openssl", "platform", ""]] + env_lws.Prepend(CPPPATH=wrapper_includes) - if env['builtin_mbedtls']: - mbedtls_includes = "#thirdparty/mbedtls/include" - env_lws.Prepend(CPPPATH=[mbedtls_includes]) + if env["platform"] == "windows" or env["platform"] == "uwp": + env_lws.Append(CPPPATH=[thirdparty_dir + helper_dir]) - if env_lws["platform"] == "windows" or env_lws["platform"] == "uwp": - env_lws.Append(CPPPATH=[thirdparty_dir + helper_dir]) + if env["platform"] == "uwp": + env_lws.Append(CCFLAGS=["/DLWS_MINGW_SUPPORT"]) - if env_lws["platform"] == "uwp": - env_lws.Append(CCFLAGS=["/DLWS_MINGW_SUPPORT"]) + env_thirdparty = env_lws.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) env_lws.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/websocket/lws_helper.cpp b/modules/websocket/lws_helper.cpp new file mode 100644 index 0000000000..b5216615e9 --- /dev/null +++ b/modules/websocket/lws_helper.cpp @@ -0,0 +1,157 @@ +/*************************************************************************/ +/* lws_helper.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. */ +/*************************************************************************/ + +#if !defined(JAVASCRIPT_ENABLED) + +#include "lws_helper.h" + +_LWSRef *_lws_create_ref(void *obj) { + + _LWSRef *out = (_LWSRef *)memalloc(sizeof(_LWSRef)); + out->is_destroying = false; + out->free_context = false; + out->is_polling = false; + out->obj = obj; + out->is_valid = true; + out->lws_structs = NULL; + out->lws_names = NULL; + return out; +} + +void _lws_free_ref(_LWSRef *ref) { + // Free strings and structs + memfree(ref->lws_structs); + memfree(ref->lws_names); + // Free ref + memfree(ref); +} + +bool _lws_destroy(struct lws_context *context, _LWSRef *ref) { + if (context == NULL || ref->is_destroying) + return false; + + if (ref->is_polling) { + ref->free_context = true; + return false; + } + + ref->is_destroying = true; + lws_context_destroy(context); + _lws_free_ref(ref); + return true; +} + +bool _lws_poll(struct lws_context *context, _LWSRef *ref) { + + ERR_FAIL_COND_V(context == NULL, false); + ERR_FAIL_COND_V(ref == NULL, false); + + ref->is_polling = true; + lws_service(context, 0); + ref->is_polling = false; + + if (!ref->free_context) + return false; // Nothing to do + + bool is_valid = ref->is_valid; // Might have been destroyed by poll + + _lws_destroy(context, ref); // Will destroy context and ref + + return is_valid; // If the object should NULL its context and ref +} + +/* + * Prepare the protocol_structs to be fed to context. + * Also prepare the protocol string used by the client. + */ +void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback, PoolVector<String> p_names, _LWSRef **r_lws_ref) { + // The input strings might go away after this call, we need to copy them. + // We will clear them when destroying the context. + int i; + int len = p_names.size(); + size_t data_size = sizeof(struct LWSPeer::PeerData); + PoolVector<String>::Read pnr = p_names.read(); + + // This is a reference connecting the object with lws keep track of status, mallocs, etc. + // Must survive as long the context. + // Must be freed manually when context creation fails. + _LWSRef *ref = _lws_create_ref(p_obj); + + // LWS protocol structs. + ref->lws_structs = (struct lws_protocols *)memalloc(sizeof(struct lws_protocols) * (len + 2)); + memset(ref->lws_structs, 0, sizeof(struct lws_protocols) * (len + 2)); + + CharString strings = p_names.join(",").ascii(); + int str_len = strings.length(); + + // Joined string of protocols, double the size: comma separated first, NULL separated last + ref->lws_names = (char *)memalloc((str_len + 1) * 2); // Plus the terminator + + char *names_ptr = ref->lws_names; + struct lws_protocols *structs_ptr = ref->lws_structs; + + // Comma separated protocols string to be used in client Sec-WebSocket-Protocol header + if (str_len > 0) + copymem(names_ptr, strings.get_data(), str_len); + names_ptr[str_len] = '\0'; // NULL terminator + + // NULL terminated protocol strings to be used in protocol structs + if (str_len > 0) + copymem(&names_ptr[str_len + 1], strings.get_data(), str_len); + names_ptr[(str_len * 2) + 1] = '\0'; // NULL terminator + int pos = str_len + 1; + + // The first protocol is the default for any http request (before upgrade). + // It is also used as the websocket protocol when no subprotocol is specified. + structs_ptr[0].name = "default"; + structs_ptr[0].callback = p_callback; + structs_ptr[0].per_session_data_size = data_size; + structs_ptr[0].rx_buffer_size = LWS_BUF_SIZE; + structs_ptr[0].tx_packet_size = LWS_PACKET_SIZE; + // Add user defined protocols + for (i = 0; i < len; i++) { + structs_ptr[i + 1].name = (const char *)&names_ptr[pos]; + structs_ptr[i + 1].callback = p_callback; + structs_ptr[i + 1].per_session_data_size = data_size; + structs_ptr[i + 1].rx_buffer_size = LWS_BUF_SIZE; + structs_ptr[i + 1].tx_packet_size = LWS_PACKET_SIZE; + pos += pnr[i].ascii().length() + 1; + names_ptr[pos - 1] = '\0'; + } + // Add protocols terminator + structs_ptr[len + 1].name = NULL; + structs_ptr[len + 1].callback = NULL; + structs_ptr[len + 1].per_session_data_size = 0; + structs_ptr[len + 1].rx_buffer_size = 0; + + *r_lws_ref = ref; +} + +#endif diff --git a/modules/websocket/lws_helper.h b/modules/websocket/lws_helper.h index 70256ccf16..fd8f85371b 100644 --- a/modules/websocket/lws_helper.h +++ b/modules/websocket/lws_helper.h @@ -49,127 +49,11 @@ struct _LWSRef { char *lws_names; }; -static _LWSRef *_lws_create_ref(void *obj) { - - _LWSRef *out = (_LWSRef *)memalloc(sizeof(_LWSRef)); - out->is_destroying = false; - out->free_context = false; - out->is_polling = false; - out->obj = obj; - out->is_valid = true; - out->lws_structs = NULL; - out->lws_names = NULL; - return out; -} - -static void _lws_free_ref(_LWSRef *ref) { - // Free strings and structs - memfree(ref->lws_structs); - memfree(ref->lws_names); - // Free ref - memfree(ref); -} - -static bool _lws_destroy(struct lws_context *context, _LWSRef *ref) { - if (context == NULL || ref->is_destroying) - return false; - - if (ref->is_polling) { - ref->free_context = true; - return false; - } - - ref->is_destroying = true; - lws_context_destroy(context); - _lws_free_ref(ref); - return true; -} - -static bool _lws_poll(struct lws_context *context, _LWSRef *ref) { - - ERR_FAIL_COND_V(context == NULL, false); - ERR_FAIL_COND_V(ref == NULL, false); - - ref->is_polling = true; - lws_service(context, 0); - ref->is_polling = false; - - if (!ref->free_context) - return false; // Nothing to do - - bool is_valid = ref->is_valid; // Might have been destroyed by poll - - _lws_destroy(context, ref); // Will destroy context and ref - - return is_valid; // If the object should NULL its context and ref -} - -/* - * Prepare the protocol_structs to be fed to context. - * Also prepare the protocol string used by the client. - */ -static void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback, PoolVector<String> p_names, _LWSRef **r_lws_ref) { - // The input strings might go away after this call, we need to copy them. - // We will clear them when destroying the context. - int i; - int len = p_names.size(); - size_t data_size = sizeof(struct LWSPeer::PeerData); - PoolVector<String>::Read pnr = p_names.read(); - - // This is a reference connecting the object with lws keep track of status, mallocs, etc. - // Must survive as long the context. - // Must be freed manually when context creation fails. - _LWSRef *ref = _lws_create_ref(p_obj); - - // LWS protocol structs. - ref->lws_structs = (struct lws_protocols *)memalloc(sizeof(struct lws_protocols) * (len + 2)); - memset(ref->lws_structs, 0, sizeof(struct lws_protocols) * (len + 2)); - - CharString strings = p_names.join(",").ascii(); - int str_len = strings.length(); - - // Joined string of protocols, double the size: comma separated first, NULL separated last - ref->lws_names = (char *)memalloc((str_len + 1) * 2); // Plus the terminator - - char *names_ptr = ref->lws_names; - struct lws_protocols *structs_ptr = ref->lws_structs; - - // Comma separated protocols string to be used in client Sec-WebSocket-Protocol header - if (str_len > 0) - copymem(names_ptr, strings.get_data(), str_len); - names_ptr[str_len] = '\0'; // NULL terminator - - // NULL terminated protocol strings to be used in protocol structs - if (str_len > 0) - copymem(&names_ptr[str_len + 1], strings.get_data(), str_len); - names_ptr[(str_len * 2) + 1] = '\0'; // NULL terminator - int pos = str_len + 1; - - // The first protocol is the default for any http request (before upgrade). - // It is also used as the websocket protocol when no subprotocol is specified. - structs_ptr[0].name = "default"; - structs_ptr[0].callback = p_callback; - structs_ptr[0].per_session_data_size = data_size; - structs_ptr[0].rx_buffer_size = LWS_BUF_SIZE; - structs_ptr[0].tx_packet_size = LWS_PACKET_SIZE; - // Add user defined protocols - for (i = 0; i < len; i++) { - structs_ptr[i + 1].name = (const char *)&names_ptr[pos]; - structs_ptr[i + 1].callback = p_callback; - structs_ptr[i + 1].per_session_data_size = data_size; - structs_ptr[i + 1].rx_buffer_size = LWS_BUF_SIZE; - structs_ptr[i + 1].tx_packet_size = LWS_PACKET_SIZE; - pos += pnr[i].ascii().length() + 1; - names_ptr[pos - 1] = '\0'; - } - // Add protocols terminator - structs_ptr[len + 1].name = NULL; - structs_ptr[len + 1].callback = NULL; - structs_ptr[len + 1].per_session_data_size = 0; - structs_ptr[len + 1].rx_buffer_size = 0; - - *r_lws_ref = ref; -} +_LWSRef *_lws_create_ref(void *obj); +void _lws_free_ref(_LWSRef *ref); +bool _lws_destroy(struct lws_context *context, _LWSRef *ref); +bool _lws_poll(struct lws_context *context, _LWSRef *ref); +void _lws_make_protocols(void *p_obj, lws_callback_function *p_callback, PoolVector<String> p_names, _LWSRef **r_lws_ref); /* clang-format off */ #define LWS_HELPER(CNAME) \ diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp index 245b28b608..b5c130b308 100644 --- a/modules/websocket/lws_peer.cpp +++ b/modules/websocket/lws_peer.cpp @@ -30,6 +30,7 @@ #ifndef JAVASCRIPT_ENABLED #include "lws_peer.h" + #include "core/io/ip.h" // Needed for socket_helpers on Android at least. UNIXes has it, just include if not windows @@ -38,7 +39,7 @@ #include <sys/socket.h> #endif -#include "drivers/unix/socket_helpers.h" +#include "drivers/unix/net_socket_posix.h" void LWSPeer::set_wsi(struct lws *p_wsi) { ERR_FAIL_COND(wsi != NULL); @@ -60,7 +61,6 @@ Error LWSPeer::read_wsi(void *in, size_t len) { ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); - PeerData *peer_data = (PeerData *)(lws_wsi_user(wsi)); uint32_t size = in_size; uint8_t is_string = lws_frame_is_binary(wsi) ? 0 : 1; @@ -88,7 +88,6 @@ Error LWSPeer::write_wsi() { ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); - PeerData *peer_data = (PeerData *)(lws_wsi_user(wsi)); PoolVector<uint8_t> tmp; int left = rbw.data_left(); uint32_t to_write = 0; @@ -119,7 +118,6 @@ Error LWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); - PeerData *peer_data = (PeerData *)lws_wsi_user(wsi); rbw.write((uint8_t *)&p_buffer_size, 4); rbw.write(p_buffer, MIN(p_buffer_size, rbw.space_left())); out_count++; @@ -132,8 +130,6 @@ Error LWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); - PeerData *peer_data = (PeerData *)lws_wsi_user(wsi); - if (in_count == 0) return ERR_UNAVAILABLE; @@ -236,7 +232,7 @@ IP_Address LWSPeer::get_connected_host() const { ERR_FAIL_COND_V(!is_connected_to_host(), IP_Address()); IP_Address ip; - int port = 0; + uint16_t port = 0; struct sockaddr_storage addr; socklen_t len = sizeof(addr); @@ -247,7 +243,7 @@ IP_Address LWSPeer::get_connected_host() const { int ret = getpeername(fd, (struct sockaddr *)&addr, &len); ERR_FAIL_COND_V(ret != 0, IP_Address()); - _set_ip_addr_port(ip, port, &addr); + NetSocketPosix::_set_ip_port(&addr, ip, port); return ip; }; @@ -257,7 +253,7 @@ uint16_t LWSPeer::get_connected_port() const { ERR_FAIL_COND_V(!is_connected_to_host(), 0); IP_Address ip; - int port = 0; + uint16_t port = 0; struct sockaddr_storage addr; socklen_t len = sizeof(addr); @@ -268,7 +264,7 @@ uint16_t LWSPeer::get_connected_port() const { int ret = getpeername(fd, (struct sockaddr *)&addr, &len); ERR_FAIL_COND_V(ret != 0, 0); - _set_ip_addr_port(ip, port, &addr); + NetSocketPosix::_set_ip_port(&addr, ip, port); return port; }; diff --git a/modules/xatlas_unwrap/SCsub b/modules/xatlas_unwrap/SCsub new file mode 100644 index 0000000000..ad364d5aaf --- /dev/null +++ b/modules/xatlas_unwrap/SCsub @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +import platform + +Import('env') +Import('env_modules') + +env_xatlas_unwrap = env_modules.Clone() + +# Thirdparty source files +if env['builtin_xatlas']: + thirdparty_dir = "#thirdparty/xatlas/" + thirdparty_sources = [ + "xatlas.cpp", + ] + thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + + env_xatlas_unwrap.Append(CPPPATH=[thirdparty_dir]) + + # upstream uses c++11 + if (not env.msvc): + env_xatlas_unwrap.Append(CXXFLAGS="-std=c++11") + + if env["platform"] == 'x11': + # if not specifically one of the *BSD, then use LINUX as default + if platform.system() == "FreeBSD": + env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_FREEBSD", "-DPOSH_COMPILER_GCC"]) + elif platform.system() == "OpenBSD": + env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_OPENBSD", "-DPOSH_COMPILER_GCC"]) + else: + env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_LINUX", "-DPOSH_COMPILER_GCC"]) + elif env["platform"] == 'osx': + env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_DARWIN", "-DPOSH_COMPILER_GCC"]) + elif env["platform"] == 'windows': + if env.msvc: + env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_WIN32", "-DNV_CC_MSVC", "-DPOSH_COMPILER_MSVC" ]) + else: + env_xatlas_unwrap.Append(CCFLAGS=["-DNV_OS_MINGW", "-DNV_CC_GNUC", "-DPOSH_COMPILER_GCC", "-U__STRICT_ANSI__"]) + env.Append(LIBS=["dbghelp"]) + + env_thirdparty = env_xatlas_unwrap.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + +# Godot source files +env_xatlas_unwrap.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/xatlas_unwrap/config.py b/modules/xatlas_unwrap/config.py new file mode 100644 index 0000000000..962d33280f --- /dev/null +++ b/modules/xatlas_unwrap/config.py @@ -0,0 +1,6 @@ +def can_build(env, platform): + return False #xatlas is buggy + #return (env['tools'] and platform not in ["android", "ios"]) + +def configure(env): + pass diff --git a/modules/xatlas_unwrap/register_types.cpp b/modules/xatlas_unwrap/register_types.cpp new file mode 100644 index 0000000000..9df16aac70 --- /dev/null +++ b/modules/xatlas_unwrap/register_types.cpp @@ -0,0 +1,133 @@ +/*************************************************************************/ +/* register_types.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 "register_types.h" +#include "core/error_macros.h" +#include "thirdparty/xatlas/xatlas.h" + +#include <stdio.h> +#include <stdlib.h> +extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, const int *p_face_materials, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y); + +bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, const int *p_face_materials, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y) { + + //set up input mesh + xatlas::InputMesh input_mesh; + input_mesh.indexData = malloc(sizeof(int) * p_index_count); + input_mesh.indexCount = p_index_count; + input_mesh.indexFormat = xatlas::IndexFormat::Float; //really xatlas? + input_mesh.faceMaterialData = (uint16_t *)malloc(sizeof(uint16_t) * p_index_count); + + for (int i = 0; i < p_index_count; i++) { + int *index = (int *)input_mesh.indexData; + index[i] = p_indices[i]; + } + for (int i = 0; i < p_index_count / 3; i++) { + uint16_t *mat_index = (uint16_t *)input_mesh.faceMaterialData; + mat_index[i] = p_face_materials[i]; + } + + input_mesh.vertexCount = p_vertex_count; + input_mesh.vertexPositionData = malloc(sizeof(float) * p_vertex_count * 3); + input_mesh.vertexPositionStride = sizeof(float) * 3; + input_mesh.vertexNormalData = malloc(sizeof(float) * p_vertex_count * 3); + input_mesh.vertexNormalStride = sizeof(float) * 3; + + //material is a better hint than this i guess? + input_mesh.vertexUvData = NULL; + input_mesh.vertexUvStride = 0; + + for (int i = 0; i < p_vertex_count * 3; i++) { + float *vertex_ptr = (float *)input_mesh.vertexPositionData; + float *normal_ptr = (float *)input_mesh.vertexNormalData; + + vertex_ptr[i] = p_vertices[i]; + normal_ptr[i] = p_normals[i]; + } + + xatlas::CharterOptions chart_options; + xatlas::PackerOptions pack_options; + + pack_options.texelArea = 1.0 / p_texel_size; + pack_options.quality = 4; + + xatlas::Atlas *atlas = xatlas::Create(); + printf("adding mesh..\n"); + xatlas::AddMeshError err = xatlas::AddMesh(atlas, input_mesh); + ERR_EXPLAINC(xatlas::StringForEnum(err.code)); + ERR_FAIL_COND_V(err.code != xatlas::AddMeshErrorCode::Success, false); + + printf("generate..\n"); + xatlas::Generate(atlas, chart_options, pack_options); + + *r_size_hint_x = xatlas::GetWidth(atlas); + *r_size_hint_y = xatlas::GetHeight(atlas); + + float w = *r_size_hint_x; + float h = *r_size_hint_y; + + printf("final texsize: %f,%f\n", w, h); + const xatlas::OutputMesh *const *output_meshes = xatlas::GetOutputMeshes(atlas); + + const xatlas::OutputMesh *output = output_meshes[0]; + + *r_vertex = (int *)malloc(sizeof(int) * output->vertexCount); + *r_uv = (float *)malloc(sizeof(float) * output->vertexCount * 2); + *r_index = (int *)malloc(sizeof(int) * output->indexCount); + + for (int i = 0; i < output->vertexCount; i++) { + (*r_vertex)[i] = output->vertexArray[i].xref; + (*r_uv)[i * 2 + 0] = output->vertexArray[i].uv[0]; + (*r_uv)[i * 2 + 1] = output->vertexArray[i].uv[1]; + } + *r_vertex_count = output->vertexCount; + + for (int i = 0; i < output->indexCount; i++) { + (*r_index)[i] = output->indexArray[i]; + } + + *r_index_count = output->indexCount; + + //xatlas::Destroy(atlas); + free((void *)input_mesh.indexData); + free((void *)input_mesh.vertexPositionData); + free((void *)input_mesh.vertexNormalData); + free((void *)input_mesh.faceMaterialData); + printf("done"); + return true; +} + +void register_xatlas_unwrap_types() { + + array_mesh_lightmap_unwrap_callback = xatlas_mesh_lightmap_unwrap_callback; +} + +void unregister_xatlas_unwrap_types() { +} diff --git a/modules/xatlas_unwrap/register_types.h b/modules/xatlas_unwrap/register_types.h new file mode 100644 index 0000000000..fd8d56fa53 --- /dev/null +++ b/modules/xatlas_unwrap/register_types.h @@ -0,0 +1,32 @@ +/*************************************************************************/ +/* register_types.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. */ +/*************************************************************************/ + +void register_xatlas_unwrap_types(); +void unregister_xatlas_unwrap_types(); diff --git a/platform/SCsub b/platform/SCsub index 0f9c2047a0..aa83154ee0 100644 --- a/platform/SCsub +++ b/platform/SCsub @@ -29,5 +29,3 @@ platform_sources.append('register_platform_apis.gen.cpp') lib = env.add_library('platform', platform_sources) env.Prepend(LIBS=lib) - -Export('env') diff --git a/platform/android/SCsub b/platform/android/SCsub index 31fee5722c..807506000f 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -1,12 +1,12 @@ #!/usr/bin/env python +Import('env') + import shutil from compat import open_utf8 from distutils.version import LooseVersion from detect import get_ndk_version -Import('env') - android_files = [ 'os_android.cpp', @@ -26,10 +26,6 @@ android_files = [ # 'power_android.cpp' ] -# env.Depends('#core/math/vector3.h', 'vector3_psp.h') - -#obj = env.SharedObject('godot_android.cpp') - env_android = env.Clone() if env['target'] == "profile": env_android.Append(CPPFLAGS=['-DPROFILER_ENABLED']) @@ -174,4 +170,4 @@ if lib_arch_dir != '': ndk_version = get_ndk_version(env["ANDROID_NDK_ROOT"]) if ndk_version != None and LooseVersion(ndk_version) >= LooseVersion("15.0.4075724"): stl_lib_path = str(env['ANDROID_NDK_ROOT']) + '/sources/cxx-stl/llvm-libc++/libs/' + lib_arch_dir + '/libc++_shared.so' - env_android.Command(out_dir + '/libc++_shared.so', stl_lib_path, Copy("$TARGET", "$SOURCE"))
\ No newline at end of file + env_android.Command(out_dir + '/libc++_shared.so', stl_lib_path, Copy("$TARGET", "$SOURCE")) diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 6ec7d27464..021a92ca1e 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -194,8 +194,8 @@ static const char *android_perms[] = { }; struct LauncherIcon { - char *option_id; - char *export_path; + const char *option_id; + const char *export_path; }; static const LauncherIcon launcher_icons[] = { @@ -577,11 +577,11 @@ class EditorExportAndroid : public EditorExportPlatform { uint32_t ofs = 8; uint32_t string_count = 0; - uint32_t styles_count = 0; + //uint32_t styles_count = 0; uint32_t string_flags = 0; uint32_t string_data_offset = 0; - uint32_t styles_offset = 0; + //uint32_t styles_offset = 0; uint32_t string_table_begins = 0; uint32_t string_table_ends = 0; Vector<uint8_t> stable_extra; @@ -631,16 +631,16 @@ class EditorExportAndroid : public EditorExportPlatform { int iofs = ofs + 8; string_count = decode_uint32(&p_manifest[iofs]); - styles_count = decode_uint32(&p_manifest[iofs + 4]); + //styles_count = decode_uint32(&p_manifest[iofs + 4]); string_flags = decode_uint32(&p_manifest[iofs + 8]); string_data_offset = decode_uint32(&p_manifest[iofs + 12]); - styles_offset = decode_uint32(&p_manifest[iofs + 16]); + //styles_offset = decode_uint32(&p_manifest[iofs + 16]); /* printf("string count: %i\n",string_count); printf("flags: %i\n",string_flags); printf("sdata ofs: %i\n",string_data_offset); printf("styles ofs: %i\n",styles_offset); - */ + */ uint32_t st_offset = iofs + 20; string_table.resize(string_count); uint32_t string_end = 0; @@ -760,7 +760,6 @@ class EditorExportAndroid : public EditorExportPlatform { // save manifest ending so we can restore it Vector<uint8_t> manifest_end; uint32_t manifest_cur_size = p_manifest.size(); - uint32_t node_size = size; manifest_end.resize(p_manifest.size() - ofs); memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size()); @@ -1055,7 +1054,12 @@ public: if (api == 0) r_features->push_back("etc"); else*/ - r_features->push_back("etc2"); + String driver = ProjectSettings::get_singleton()->get("rendering/quality/driver/driver_name"); + if (driver == "GLES2") { + r_features->push_back("etc"); + } else { + r_features->push_back("etc2"); + } Vector<String> abis = get_enabled_abis(p_preset); for (int i = 0; i < abis.size(); ++i) { @@ -1082,7 +1086,7 @@ public: r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_large"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_xlarge"), true)); - for (int i = 0; i < sizeof(launcher_icons) / sizeof(launcher_icons[0]); ++i) { + for (unsigned int i = 0; i < sizeof(launcher_icons) / sizeof(launcher_icons[0]); ++i) { r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icons[i].option_id, PROPERTY_HINT_FILE, "*.png"), "")); } @@ -1486,7 +1490,7 @@ public: if (file == "res/drawable/icon.png") { bool found = false; - for (int i = 0; i < sizeof(launcher_icons) / sizeof(launcher_icons[0]); ++i) { + for (unsigned int i = 0; i < sizeof(launcher_icons) / sizeof(launcher_icons[0]); ++i) { String icon_path = String(p_preset->get(launcher_icons[i].option_id)).strip_edges(); if (icon_path != "" && icon_path.ends_with(".png")) { FileAccess *f = FileAccess::open(icon_path, FileAccess::READ); @@ -1620,7 +1624,7 @@ public: APKExportData ed; ed.ep = &ep; ed.apk = unaligned_apk; - for (int i = 0; i < sizeof(launcher_icons) / sizeof(launcher_icons[0]); ++i) { + for (unsigned int i = 0; i < sizeof(launcher_icons) / sizeof(launcher_icons[0]); ++i) { String icon_path = String(p_preset->get(launcher_icons[i].option_id)).strip_edges(); if (icon_path != "" && icon_path.ends_with(".png") && FileAccess::exists(icon_path)) { Vector<uint8_t> data = FileAccess::get_file_as_array(icon_path); diff --git a/platform/android/java/src/org/godotengine/godot/Godot.java b/platform/android/java/src/org/godotengine/godot/Godot.java index 92c9be5d43..c23037f3e0 100644 --- a/platform/android/java/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/src/org/godotengine/godot/Godot.java @@ -184,6 +184,9 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC protected void onMainActivityResult(int requestCode, int resultCode, Intent data) { } + protected void onMainRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + } + protected void onMainPause() {} protected void onMainResume() {} protected void onMainDestroy() {} @@ -251,6 +254,13 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC } }; + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + for (int i = 0; i < singleton_count; i++) { + singletons[i].onMainRequestPermissionsResult(requestCode, permissions, grantResults); + } + }; + public void onVideoInit() { boolean use_gl3 = getGLESVersionCode() >= 0x00030000; diff --git a/platform/haiku/detect.py b/platform/haiku/detect.py index 7ecdd2bb11..8d704ac657 100644 --- a/platform/haiku/detect.py +++ b/platform/haiku/detect.py @@ -148,7 +148,7 @@ def configure(env): ## Flags env.Append(CPPPATH=['#platform/haiku']) - env.Append(CPPFLAGS=['-DUNIX_ENABLED', '-DOPENGL_ENABLED', '-DGLES_ENABLED', '-DGLES_OVER_GL']) + env.Append(CPPFLAGS=['-DUNIX_ENABLED', '-DOPENGL_ENABLED', '-DGLES_ENABLED']) env.Append(CPPFLAGS=['-DMEDIA_KIT_ENABLED']) # env.Append(CCFLAGS=['-DFREETYPE_ENABLED']) env.Append(CPPFLAGS=['-DPTHREAD_NO_RENAME']) # TODO: enable when we have pthread_setname_np diff --git a/platform/haiku/platform_config.h b/platform/haiku/platform_config.h index 72c8ee2535..b00510f5a1 100644 --- a/platform/haiku/platform_config.h +++ b/platform/haiku/platform_config.h @@ -33,5 +33,5 @@ // for ifaddrs.h needed in drivers/unix/ip_unix.cpp #define _BSD_SOURCE 1 -#define GLES3_INCLUDE_H "glad/glad.h" -#define GLES2_INCLUDE_H "glad/glad.h" +#define GLES3_INCLUDE_H "thirdparty/glad/glad/glad.h" +#define GLES2_INCLUDE_H "thirdparty/glad/glad/glad.h" diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub index b96bec16b4..debf051eda 100644 --- a/platform/iphone/SCsub +++ b/platform/iphone/SCsub @@ -1,8 +1,9 @@ #!/usr/bin/env python -import os Import('env') +import os + iphone_lib = [ 'godot_iphone.cpp', 'os_iphone.cpp', diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp index aae9d97a28..63bc4a519b 100644 --- a/platform/iphone/export/export.cpp +++ b/platform/iphone/export/export.cpp @@ -203,7 +203,7 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_40x40", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_80x80", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight on devices with retina display - for (int i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) { + for (unsigned int i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) { r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png"), "")); } @@ -353,7 +353,7 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr DirAccess *da = DirAccess::open(p_iconset_dir); ERR_FAIL_COND_V(!da, ERR_CANT_OPEN); - for (int i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) { + for (unsigned int i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) { IconInfo info = icon_infos[i]; String icon_path = p_preset->get(info.preset_key); if (icon_path.length() == 0) { @@ -403,7 +403,7 @@ Error EditorExportPlatformIOS::_export_loading_screens(const Ref<EditorExportPre DirAccess *da = DirAccess::open(p_dest_dir); ERR_FAIL_COND_V(!da, ERR_CANT_OPEN); - for (int i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) { + for (unsigned int i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) { LoadingScreenInfo info = loading_screen_infos[i]; String loading_screen_file = p_preset->get(info.preset_key); if (loading_screen_file.size() > 0) { @@ -490,7 +490,7 @@ private: static String _hex_pad(uint32_t num) { Vector<char> ret; ret.resize(sizeof(num) * 2); - for (int i = 0; i < sizeof(num) * 2; ++i) { + for (unsigned int i = 0; i < sizeof(num) * 2; ++i) { uint8_t four_bits = (num >> (sizeof(num) * 8 - (i + 1) * 4)) & 0xF; ret.write[i] = _hex_char(four_bits); } diff --git a/platform/javascript/http_client_javascript.cpp b/platform/javascript/http_client_javascript.cpp index 4fb41d4dc9..ccf4f8a11b 100644 --- a/platform/javascript/http_client_javascript.cpp +++ b/platform/javascript/http_client_javascript.cpp @@ -237,7 +237,7 @@ Error HTTPClient::poll() { case STATUS_CONNECTION_ERROR: return ERR_CONNECTION_ERROR; - case STATUS_REQUESTING: + case STATUS_REQUESTING: { #ifdef DEBUG_ENABLED if (!has_polled) { @@ -281,6 +281,10 @@ Error HTTPClient::poll() { godot_xhr_get_response(xhr_id, write.ptr(), polled_response.size()); write = PoolByteArray::Write(); break; + } + + default: + ERR_FAIL_V(ERR_BUG); } return OK; } diff --git a/platform/javascript/javascript_eval.cpp b/platform/javascript/javascript_eval.cpp index 07b4c192e6..9b8174cc71 100644 --- a/platform/javascript/javascript_eval.cpp +++ b/platform/javascript/javascript_eval.cpp @@ -140,8 +140,9 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) { case Variant::POOL_BYTE_ARRAY: arr_write = PoolByteArray::Write(); return arr; + default: + return Variant(); } - return Variant(); } #endif // JAVASCRIPT_EVAL_ENABLED diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index b0ec3c4245..7c7aeac980 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -31,12 +31,12 @@ #include "os_javascript.h" #include "core/io/file_access_buffered_fa.h" -#include "gles2/rasterizer_gles2.h" -#include "gles3/rasterizer_gles3.h" +#include "drivers/gles2/rasterizer_gles2.h" +#include "drivers/gles3/rasterizer_gles3.h" +#include "drivers/unix/dir_access_unix.h" +#include "drivers/unix/file_access_unix.h" #include "main/main.h" #include "servers/visual/visual_server_raster.h" -#include "unix/dir_access_unix.h" -#include "unix/file_access_unix.h" #include <emscripten.h> #include <png.h> @@ -121,14 +121,14 @@ void OS_JavaScript::set_window_size(const Size2 p_size) { emscripten_exit_soft_fullscreen(); window_maximized = false; } - emscripten_set_canvas_size(p_size.x, p_size.y); + emscripten_set_canvas_element_size(NULL, p_size.x, p_size.y); } } Size2 OS_JavaScript::get_window_size() const { - int canvas[3]; - emscripten_get_canvas_size(canvas, canvas + 1, canvas + 2); + int canvas[2]; + emscripten_get_canvas_element_size(NULL, canvas, canvas + 1); return Size2(canvas[0], canvas[1]); } @@ -378,15 +378,13 @@ static void set_css_cursor(const char *p_cursor) { /* clang-format on */ } -static const char *get_css_cursor() { +static bool is_css_cursor_hidden() { - char cursor[16]; /* clang-format off */ - EM_ASM_INT({ - stringToUTF8(Module.canvas.style.cursor ? Module.canvas.style.cursor : 'auto', $0, 16); - }, cursor); + return EM_ASM_INT({ + return Module.canvas.style.cursor === 'none'; + }); /* clang-format on */ - return cursor; } void OS_JavaScript::set_cursor_shape(CursorShape p_shape) { @@ -430,7 +428,7 @@ void OS_JavaScript::set_mouse_mode(OS::MouseMode p_mode) { OS::MouseMode OS_JavaScript::get_mouse_mode() const { - if (String::utf8(get_css_cursor()) == "none") + if (is_css_cursor_hidden()) return MOUSE_MODE_HIDDEN; EmscriptenPointerlockChangeEvent ev; @@ -835,13 +833,13 @@ bool OS_JavaScript::main_loop_iterate() { strategy.canvasResizedCallback = NULL; emscripten_enter_soft_fullscreen(NULL, &strategy); } else { - emscripten_set_canvas_size(windowed_size.width, windowed_size.height); + emscripten_set_canvas_element_size(NULL, windowed_size.width, windowed_size.height); } just_exited_fullscreen = false; } - int canvas[3]; - emscripten_get_canvas_size(canvas, canvas + 1, canvas + 2); + int canvas[2]; + emscripten_get_canvas_element_size(NULL, canvas, canvas + 1); video_mode.width = canvas[0]; video_mode.height = canvas[1]; if (!window_maximized && !video_mode.fullscreen && !just_exited_fullscreen && !entering_fullscreen) { diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h index ddcbf8c7c9..84075898ac 100644 --- a/platform/javascript/os_javascript.h +++ b/platform/javascript/os_javascript.h @@ -32,10 +32,10 @@ #define OS_JAVASCRIPT_H #include "audio_driver_javascript.h" +#include "drivers/unix/os_unix.h" #include "main/input_default.h" #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" -#include "unix/os_unix.h" #include <emscripten/html5.h> diff --git a/platform/osx/SCsub b/platform/osx/SCsub index 5c973c30c2..dc407eee9e 100644 --- a/platform/osx/SCsub +++ b/platform/osx/SCsub @@ -1,8 +1,8 @@ #!/usr/bin/env python -import os Import('env') +import os from platform_methods import run_in_subprocess import platform_osx_builders diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp index b0232e2990..27b4fdc228 100644 --- a/platform/osx/export/export.cpp +++ b/platform/osx/export/export.cpp @@ -461,7 +461,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p fi.internal_fa = info.internal_fa; fi.external_fa = info.external_fa; - int zerr = zipOpenNewFileInZip(dst_pkg_zip, + zipOpenNewFileInZip(dst_pkg_zip, file.utf8().get_data(), &fi, NULL, @@ -472,7 +472,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p Z_DEFLATED, Z_DEFAULT_COMPRESSION); - zerr = zipWriteInFileInZip(dst_pkg_zip, data.ptr(), data.size()); + zipWriteInFileInZip(dst_pkg_zip, data.ptr(), data.size()); zipCloseFileInZip(dst_pkg_zip); } } diff --git a/platform/osx/platform_config.h b/platform/osx/platform_config.h index 3f72831d77..1d32d5b5b9 100644 --- a/platform/osx/platform_config.h +++ b/platform/osx/platform_config.h @@ -30,6 +30,6 @@ #include <alloca.h> -#define GLES3_INCLUDE_H "glad/glad.h" -#define GLES2_INCLUDE_H "glad/glad.h" +#define GLES3_INCLUDE_H "thirdparty/glad/glad/glad.h" +#define GLES2_INCLUDE_H "thirdparty/glad/glad/glad.h" #define PTHREAD_RENAME_SELF diff --git a/platform/server/SCsub b/platform/server/SCsub index 0788ad75ae..c9082f9b3a 100644 --- a/platform/server/SCsub +++ b/platform/server/SCsub @@ -2,11 +2,9 @@ Import('env') - common_server = [\ "os_server.cpp",\ "#platform/x11/crash_handler_x11.cpp", "#platform/x11/power_x11.cpp", ] - prog = env.add_program('#bin/godot_server', ['godot_server.cpp'] + common_server) diff --git a/platform/server/detect.py b/platform/server/detect.py index e921bf4a5f..597a2ff6a0 100644 --- a/platform/server/detect.py +++ b/platform/server/detect.py @@ -29,9 +29,7 @@ def get_opts(): def get_flags(): - return [ - ("module_mobile_vr_enabled", False), - ] + return [] def configure(env): diff --git a/platform/windows/SCsub b/platform/windows/SCsub index 5dfb1592e0..e07d373c4b 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -1,8 +1,8 @@ #!/usr/bin/env python -import os Import('env') +import os from platform_methods import run_in_subprocess import platform_windows_builders @@ -19,9 +19,7 @@ common_win = [ ] res_file = 'godot_res.rc' - res_target = "godot_res" + env["OBJSUFFIX"] - res_obj = env.RES(res_target, res_file) prog = env.add_program('#bin/godot', common_win + res_obj, PROGSUFFIX=env["PROGSUFFIX"]) diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index d575525f93..e8c209c0fc 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -458,7 +458,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) */ } - if (window_has_focus && main_loop) + if (window_has_focus && main_loop && mm->get_relative() != Vector2()) input->parse_input_event(mm); } delete[] lpb; @@ -697,7 +697,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) last_button_state = mb->get_button_mask(); mb->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); - if (mouse_mode == MOUSE_MODE_CAPTURED) { + if (mouse_mode == MOUSE_MODE_CAPTURED && !use_raw_input) { mb->set_position(Vector2(old_x, old_y)); } diff --git a/platform/windows/platform_config.h b/platform/windows/platform_config.h index d100385e80..aa020ed470 100644 --- a/platform/windows/platform_config.h +++ b/platform/windows/platform_config.h @@ -32,5 +32,5 @@ //#else //#include <alloca.h> //#endif -#define GLES3_INCLUDE_H "glad/glad.h" -#define GLES2_INCLUDE_H "glad/glad.h" +#define GLES3_INCLUDE_H "thirdparty/glad/glad/glad.h" +#define GLES2_INCLUDE_H "thirdparty/glad/glad/glad.h" diff --git a/platform/x11/SCsub b/platform/x11/SCsub index d3901eb798..97d3d1b514 100644 --- a/platform/x11/SCsub +++ b/platform/x11/SCsub @@ -1,8 +1,8 @@ #!/usr/bin/env python -import os Import('env') +import os from platform_methods import run_in_subprocess import platform_x11_builders diff --git a/platform/x11/detect.py b/platform/x11/detect.py index 905546e724..ee59e9b5a1 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -250,7 +250,8 @@ def configure(env): if (os.system("pkg-config --exists alsa") == 0): # 0 means found print("Enabling ALSA") env.Append(CPPFLAGS=["-DALSA_ENABLED", "-DALSAMIDI_ENABLED"]) - env.ParseConfig('pkg-config alsa --cflags --libs') + # Don't parse --cflags, we don't need to add /usr/include/alsa to include path + env.ParseConfig('pkg-config alsa --libs') else: print("ALSA libraries not found, disabling driver") @@ -278,7 +279,7 @@ def configure(env): env.ParseConfig('pkg-config zlib --cflags --libs') env.Append(CPPPATH=['#platform/x11']) - env.Append(CPPFLAGS=['-DX11_ENABLED', '-DUNIX_ENABLED', '-DOPENGL_ENABLED', '-DGLES_ENABLED', '-DGLES_OVER_GL']) + env.Append(CPPFLAGS=['-DX11_ENABLED', '-DUNIX_ENABLED', '-DOPENGL_ENABLED', '-DGLES_ENABLED']) env.Append(LIBS=['GL', 'pthread']) if (platform.system() == "Linux"): diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 3d05a650da..5be0b9304a 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -1995,15 +1995,9 @@ void OS_X11::process_xevents() { } break; case MotionNotify: { - // FUCK YOU X11 API YOU SERIOUSLY GROSS ME OUT - // YOU ARE AS GROSS AS LOOKING AT A PUTRID PILE - // OF POOP STICKING OUT OF A CLOGGED TOILET - // HOW THE FUCK I AM SUPPOSED TO KNOW WHICH ONE - // OF THE MOTION NOTIFY EVENTS IS THE ONE GENERATED - // BY WARPING THE MOUSE POINTER? - // YOU ARE FORCING ME TO FILTER ONE BY ONE TO FIND IT - // PLEASE DO ME A FAVOR AND DIE DROWNED IN A FECAL - // MOUNTAIN BECAUSE THAT'S WHERE YOU BELONG. + // The X11 API requires filtering one-by-one through the motion + // notify events, in order to figure out which event is the one + // generated by warping the mouse pointer. while (true) { if (mouse_mode == MOUSE_MODE_CAPTURED && event.xmotion.x == current_videomode.width / 2 && event.xmotion.y == current_videomode.height / 2) { diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index 47ff257455..bb8411e213 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -34,17 +34,17 @@ #include "context_gl_x11.h" #include "core/os/input.h" #include "crash_handler_x11.h" -#include "drivers/unix/os_unix.h" -#include "servers/visual_server.h" -//#include "servers/visual/visual_server_wrap_mt.h" #include "drivers/alsa/audio_driver_alsa.h" #include "drivers/alsamidi/alsa_midi.h" #include "drivers/pulseaudio/audio_driver_pulseaudio.h" +#include "drivers/unix/os_unix.h" #include "joypad_linux.h" #include "main/input_default.h" #include "power_x11.h" #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" +#include "servers/visual_server.h" +//#include "servers/visual/visual_server_wrap_mt.h" #include <X11/Xcursor/Xcursor.h> #include <X11/Xlib.h> @@ -145,7 +145,6 @@ class OS_X11 : public OS_Unix { void handle_key_event(XKeyEvent *p_event, bool p_echo = false); void process_xevents(); virtual void delete_main_loop(); - IP_Unix *ip_unix; bool force_quit; bool minimized; @@ -177,8 +176,6 @@ class OS_X11 : public OS_Unix { AudioDriverPulseAudio driver_pulseaudio; #endif - Atom net_wm_icon; - PowerX11 *power_manager; bool layered_window; @@ -186,8 +183,6 @@ class OS_X11 : public OS_Unix { CrashHandler crash_handler; int video_driver_index; - int audio_driver_index; - unsigned int capture_idle; bool maximized; //void set_wm_border(bool p_enabled); void set_wm_fullscreen(bool p_enabled); diff --git a/platform/x11/platform_config.h b/platform/x11/platform_config.h index b757be49c3..f6d7f5a8cd 100644 --- a/platform/x11/platform_config.h +++ b/platform/x11/platform_config.h @@ -36,5 +36,5 @@ #define PTHREAD_BSD_SET_NAME #endif -#define GLES3_INCLUDE_H "glad/glad.h" -#define GLES2_INCLUDE_H "glad/glad.h" +#define GLES3_INCLUDE_H "thirdparty/glad/glad/glad.h" +#define GLES2_INCLUDE_H "thirdparty/glad/glad/glad.h" diff --git a/scene/2d/SCsub b/scene/2d/SCsub index bf9125be7f..b01e2fd54d 100644 --- a/scene/2d/SCsub +++ b/scene/2d/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.scene_sources, "*.cpp") - -Export('env') diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index a8e0f0d07f..d29c6b37d5 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -507,10 +507,6 @@ static float rand_from_seed(uint32_t &seed) { return float(seed % uint32_t(65536)) / 65535.0; } -static float rand_from_seed_m1_p1(uint32_t &seed) { - return rand_from_seed(seed) * 2.0 - 1.0; -} - void CPUParticles2D::_particles_process(float p_delta) { p_delta *= speed_scale; diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 71bd51507e..5b8d10ea85 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -257,7 +257,6 @@ void TileMap::update_dirty_quadrants() { VisualServer *vs = VisualServer::get_singleton(); Physics2DServer *ps = Physics2DServer::get_singleton(); Vector2 tofs = get_cell_draw_offset(); - Vector2 tcenter = cell_size / 2; Transform2D nav_rel; if (navigation) nav_rel = get_relative_transform_to_parent(navigation); diff --git a/scene/3d/SCsub b/scene/3d/SCsub index 4008f4f196..35cc7479d8 100644 --- a/scene/3d/SCsub +++ b/scene/3d/SCsub @@ -2,9 +2,7 @@ Import('env') - if env['disable_3d']: - env.scene_sources.append("3d/spatial.cpp") env.scene_sources.append("3d/skeleton.cpp") env.scene_sources.append("3d/particles.cpp") @@ -12,5 +10,3 @@ if env['disable_3d']: env.scene_sources.append("3d/scenario_fx.cpp") else: env.add_source_files(env.scene_sources, "*.cpp") - -Export('env') diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index 386f2a4348..b1f90b72e7 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -417,7 +417,7 @@ void AudioStreamPlayer3D::_notification(int p_what) { } } - for (int k = 0; k < cc; k++) { + for (unsigned int k = 0; k < cc; k++) { output.vol[k] *= multiplier; } diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp index 712f0ba78b..ec51c31674 100644 --- a/scene/3d/cpu_particles.cpp +++ b/scene/3d/cpu_particles.cpp @@ -471,10 +471,6 @@ static float rand_from_seed(uint32_t &seed) { return float(seed % uint32_t(65536)) / 65535.0; } -static float rand_from_seed_m1_p1(uint32_t &seed) { - return rand_from_seed(seed) * 2.0 - 1.0; -} - void CPUParticles::_particles_process(float p_delta) { p_delta *= speed_scale; diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp index d674958d33..7e1d60ab8e 100644 --- a/scene/3d/light.cpp +++ b/scene/3d/light.cpp @@ -163,6 +163,11 @@ void Light::_update_visibility() { if (!is_inside_tree()) return; + // FIXME: Since the call to VS::instance_light_set_enabled was disabled below, + // the whole logic became pointless so editor_ok triggers unused variable warnings. + // Commenting out for now but this should be fixed/reimplemented so that editor_only + // works as expected (GH-17989). + /* bool editor_ok = true; #ifdef TOOLS_ENABLED @@ -180,6 +185,7 @@ void Light::_update_visibility() { #endif //VS::get_singleton()->instance_light_set_enabled(get_instance(),is_visible_in_tree() && editor_ok); + */ _change_notify("geometry/visible"); } diff --git a/scene/3d/navigation.cpp b/scene/3d/navigation.cpp index 54f74c2df3..6e7b372647 100644 --- a/scene/3d/navigation.cpp +++ b/scene/3d/navigation.cpp @@ -563,7 +563,6 @@ Vector3 Navigation::get_closest_point_to_segment(const Vector3 &p_from, const Ve bool use_collision = p_use_collision; Vector3 closest_point; float closest_point_d = 1e20; - NavMesh *closest_navmesh = NULL; for (Map<int, NavMesh>::Element *E = navmesh_map.front(); E; E = E->next()) { @@ -582,12 +581,10 @@ Vector3 Navigation::get_closest_point_to_segment(const Vector3 &p_from, const Ve closest_point = inters; use_collision = true; closest_point_d = p_from.distance_to(inters); - closest_navmesh = p.owner; } else if (closest_point_d > inters.distance_to(p_from)) { closest_point = inters; closest_point_d = p_from.distance_to(inters); - closest_navmesh = p.owner; } } } @@ -605,7 +602,6 @@ Vector3 Navigation::get_closest_point_to_segment(const Vector3 &p_from, const Ve closest_point_d = d; closest_point = b; - closest_navmesh = p.owner; } } } diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index d7bd89625f..0fb0869979 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -2287,6 +2287,8 @@ void PhysicalBone::_reload_joint() { } } break; + case JOINT_TYPE_NONE: { + } break; } } @@ -2342,6 +2344,8 @@ void PhysicalBone::set_joint_type(JointType p_joint_type) { case JOINT_TYPE_6DOF: joint_data = memnew(SixDOFJointData); break; + case JOINT_TYPE_NONE: + break; } _reload_joint(); @@ -2505,12 +2509,12 @@ PhysicalBone::PhysicalBone() : gizmo_move_joint(false), #endif joint_data(NULL), + parent_skeleton(NULL), static_body(false), - simulate_physics(false), _internal_static_body(false), + simulate_physics(false), _internal_simulate_physics(false), bone_id(-1), - parent_skeleton(NULL), bone_name(""), bounce(0), mass(1), diff --git a/scene/3d/spring_arm.cpp b/scene/3d/spring_arm.cpp index 818e7f9217..f74784c2f9 100644 --- a/scene/3d/spring_arm.cpp +++ b/scene/3d/spring_arm.cpp @@ -36,8 +36,9 @@ SpringArm::SpringArm() : spring_length(1), - mask(1), current_spring_length(0), + keep_child_basis(false), + mask(1), margin(0.01) {} void SpringArm::_notification(int p_what) { diff --git a/scene/3d/spring_arm.h b/scene/3d/spring_arm.h index 24d912d371..e0c3f2992d 100644 --- a/scene/3d/spring_arm.h +++ b/scene/3d/spring_arm.h @@ -39,8 +39,8 @@ class SpringArm : public Spatial { Ref<Shape> shape; Set<RID> excluded_objects; float spring_length; - bool keep_child_basis; float current_spring_length; + bool keep_child_basis; uint32_t mask; float margin; diff --git a/scene/SCsub b/scene/SCsub index 5d81e818ba..d8839ce3a8 100644 --- a/scene/SCsub +++ b/scene/SCsub @@ -4,16 +4,19 @@ Import('env') env.scene_sources = [] - # Thirdparty code thirdparty_dir = "#thirdparty/misc/" thirdparty_sources = [ + # C++ sources + "easing_equations.cpp", # C sources "mikktspace.c", ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env.add_source_files(env.scene_sources, thirdparty_sources) +env_thirdparty = env.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.scene_sources, thirdparty_sources) # Godot's own sources env.add_source_files(env.scene_sources, "*.cpp") @@ -32,5 +35,3 @@ SConscript('resources/SCsub') # Build it all as a library lib = env.add_library("scene", env.scene_sources) env.Prepend(LIBS=[lib]) - -Export('env') diff --git a/scene/animation/SCsub b/scene/animation/SCsub index bf9125be7f..b01e2fd54d 100644 --- a/scene/animation/SCsub +++ b/scene/animation/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.scene_sources, "*.cpp") - -Export('env') diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 7d91703cf8..102f05a146 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -1451,6 +1451,7 @@ void AnimationPlayer::_set_process(bool p_process, bool p_force) { case ANIMATION_PROCESS_PHYSICS: set_physics_process_internal(p_process && active); break; case ANIMATION_PROCESS_IDLE: set_process_internal(p_process && active); break; + case ANIMATION_PROCESS_MANUAL: break; } processing = p_process; diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp index 179f5d9698..524784df53 100644 --- a/scene/animation/animation_tree_player.cpp +++ b/scene/animation/animation_tree_player.cpp @@ -877,6 +877,7 @@ void AnimationTreePlayer::_process_animation(float p_delta) { tr.track->object->call(method, args[0], args[1], args[2], args[3], args[4]); } } break; + default: {} } } } diff --git a/scene/animation/skeleton_ik.cpp b/scene/animation/skeleton_ik.cpp index 69975e6195..83f45afac8 100644 --- a/scene/animation/skeleton_ik.cpp +++ b/scene/animation/skeleton_ik.cpp @@ -418,11 +418,11 @@ void SkeletonIK::_notification(int p_what) { SkeletonIK::SkeletonIK() : Node(), interpolation(1), - skeleton(NULL), - target_node_override(NULL), use_magnet(false), min_distance(0.01), max_iterations(10), + skeleton(NULL), + target_node_override(NULL), task(NULL) { set_process_priority(1); diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 82e2bb93e2..3521782417 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -277,7 +277,10 @@ Variant &Tween::_get_initial_val(InterpolateData &p_data) { ERR_FAIL_COND_V(error.error != Variant::CallError::CALL_OK, p_data.initial_val); } return initial_val; - } break; + } + + case INTER_CALLBACK: + break; } return p_data.delta_val; } @@ -313,7 +316,7 @@ Variant &Tween::_get_delta_val(InterpolateData &p_data) { if (final_val.get_type() == Variant::INT) final_val = final_val.operator real_t(); _calc_delta_val(p_data.initial_val, final_val, p_data.delta_val); return p_data.delta_val; - } break; + } case TARGETING_PROPERTY: case TARGETING_METHOD: { @@ -325,7 +328,10 @@ Variant &Tween::_get_delta_val(InterpolateData &p_data) { //_calc_delta_val(p_data.initial_val, p_data.final_val, p_data.delta_val); _calc_delta_val(initial_val, p_data.final_val, p_data.delta_val); return p_data.delta_val; - } break; + } + + case INTER_CALLBACK: + break; } return p_data.initial_val; } @@ -857,12 +863,8 @@ bool Tween::seek(real_t p_time) { data.finish = false; } - switch (data.type) { - case INTER_PROPERTY: - case INTER_METHOD: - break; - case INTER_CALLBACK: - continue; + if (data.type == INTER_CALLBACK) { + continue; } Variant result = _run_equation(data); diff --git a/scene/audio/SCsub b/scene/audio/SCsub index bf9125be7f..b01e2fd54d 100644 --- a/scene/audio/SCsub +++ b/scene/audio/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.scene_sources, "*.cpp") - -Export('env') diff --git a/scene/gui/SCsub b/scene/gui/SCsub index bf9125be7f..b01e2fd54d 100644 --- a/scene/gui/SCsub +++ b/scene/gui/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.scene_sources, "*.cpp") - -Export('env') diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 59590ea67b..71fb97c2c6 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -315,6 +315,14 @@ void BaseButton::set_disabled(bool p_disabled) { return; status.disabled = p_disabled; + if (p_disabled) { + if (!toggle_mode) { + status.pressed = false; + } + status.press_attempt = false; + status.pressing_inside = false; + status.pressing_button = 0; + } update(); _change_notify("disabled"); } @@ -360,7 +368,9 @@ BaseButton::DrawMode BaseButton::get_draw_mode() const { return DRAW_DISABLED; }; - if (status.press_attempt == false && status.hovering && !status.pressed) { + if (status.press_attempt == false && status.hovering) { + if (status.pressed) + return DRAW_HOVER_PRESSED; return DRAW_HOVER; } else { @@ -536,6 +546,7 @@ void BaseButton::_bind_methods() { BIND_ENUM_CONSTANT(DRAW_PRESSED); BIND_ENUM_CONSTANT(DRAW_HOVER); BIND_ENUM_CONSTANT(DRAW_DISABLED); + BIND_ENUM_CONSTANT(DRAW_HOVER_PRESSED); BIND_ENUM_CONSTANT(ACTION_MODE_BUTTON_PRESS); BIND_ENUM_CONSTANT(ACTION_MODE_BUTTON_RELEASE); diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h index 79638bbcce..176d9fc213 100644 --- a/scene/gui/base_button.h +++ b/scene/gui/base_button.h @@ -85,6 +85,7 @@ public: DRAW_PRESSED, DRAW_HOVER, DRAW_DISABLED, + DRAW_HOVER_PRESSED, }; DrawMode get_draw_mode() const; diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index dd6d66ac62..2d17fb1391 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -88,6 +88,21 @@ void Button::_notification(int p_what) { if (has_color("icon_color_normal")) color_icon = get_color("icon_color_normal"); } break; + case DRAW_HOVER_PRESSED: { + if (has_stylebox("hover_pressed") && has_stylebox_override("hover_pressed")) { + style = get_stylebox("hover_pressed"); + if (!flat) + style->draw(ci, Rect2(Point2(0, 0), size)); + if (has_color("font_color_hover_pressed")) + color = get_color("font_color_hover_pressed"); + else + color = get_color("font_color"); + if (has_color("icon_color_hover_pressed")) + color_icon = get_color("icon_color_hover_pressed"); + + break; + } + } case DRAW_PRESSED: { style = get_stylebox("pressed"); diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 537a16fbc3..03eee9c6d8 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -33,6 +33,11 @@ #include "core/os/input.h" #include "core/os/keyboard.h" #include "core/os/os.h" + +#ifdef TOOLS_ENABLED +#include "editor_settings.h" +#endif + #include "scene/gui/separator.h" #include "scene/main/viewport.h" @@ -52,6 +57,16 @@ void ColorPicker::_notification(int p_what) { bt_add_preset->set_icon(get_icon("add_preset")); _update_color(); + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + PoolColorArray saved_presets = EditorSettings::get_singleton()->get_project_metadata("color_picker", "presets", PoolColorArray()); + + for (int i = 0; i < saved_presets.size(); i++) { + add_preset(saved_presets[i]); + } + } +#endif } break; case NOTIFICATION_PARENTED: { @@ -186,9 +201,22 @@ void ColorPicker::_update_presets() { preset->draw_texture_rect(get_icon("preset_bg", "ColorPicker"), Rect2(Point2(), preset_size), true); +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + PoolColorArray arr_to_save = PoolColorArray(); + + for (int i = 0; i < presets.size(); i++) { + preset->draw_rect(Rect2(Point2(size.width * i, 0), size), presets[i]); + arr_to_save.insert(i, presets[i]); + } + + EditorSettings::get_singleton()->set_project_metadata("color_picker", "presets", arr_to_save); + } +#else for (int i = 0; i < presets.size(); i++) { preset->draw_rect(Rect2(Point2(size.width * i, 0), size), presets[i]); } +#endif } void ColorPicker::_text_type_toggled() { diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 283d66d8de..1e9f4df4a3 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -330,6 +330,10 @@ void FileDialog::deselect_items() { case MODE_OPEN_DIR: get_ok()->set_text(RTR("Select Current Folder")); break; + case MODE_OPEN_ANY: + case MODE_SAVE_FILE: + // FIXME: Implement, or refactor to avoid duplication with set_mode + break; } } } diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp index 8560efdde5..d38a067fef 100644 --- a/scene/gui/link_button.cpp +++ b/scene/gui/link_button.cpp @@ -91,6 +91,7 @@ void LinkButton::_notification(int p_what) { do_underline = underline_mode != UNDERLINE_MODE_NEVER; } break; + case DRAW_HOVER_PRESSED: break; // Not used in this class case DRAW_DISABLED: { color = get_color("font_color_disabled"); diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp index 09d8664240..e862743934 100644 --- a/scene/gui/range.cpp +++ b/scene/gui/range.cpp @@ -30,6 +30,19 @@ #include "range.h" +String Range::get_configuration_warning() const { + String warning = Control::get_configuration_warning(); + + if (shared->exp_ratio && shared->min <= 0) { + if (warning != String()) { + warning += "\n"; + } + warning += TTR("If exp_edit is true min_value must be > 0."); + } + + return warning; +} + void Range::_value_changed_notify() { _value_changed(shared->val); @@ -66,10 +79,11 @@ void Range::Shared::emit_changed(const char *p_what) { } void Range::set_value(double p_val) { + if (shared->step > 0) + p_val = Math::round(p_val / shared->step) * shared->step; - if (_rounded_values) { + if (_rounded_values) p_val = Math::round(p_val); - } if (!shared->allow_greater && p_val > shared->max - shared->page) p_val = shared->max - shared->page; @@ -90,6 +104,8 @@ void Range::set_min(double p_min) { set_value(shared->val); shared->emit_changed("min"); + + update_configuration_warning(); } void Range::set_max(double p_max) { @@ -277,6 +293,8 @@ bool Range::is_using_rounded_values() const { void Range::set_exp_ratio(bool p_enable) { shared->exp_ratio = p_enable; + + update_configuration_warning(); } bool Range::is_ratio_exp() const { diff --git a/scene/gui/range.h b/scene/gui/range.h index 125f559248..58f15c8aa8 100644 --- a/scene/gui/range.h +++ b/scene/gui/range.h @@ -97,6 +97,8 @@ public: void share(Range *p_range); void unshare(); + virtual String get_configuration_warning() const; + Range(); ~Range(); }; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 1069470ed3..bb36852cf9 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -421,7 +421,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & int cw = 0; - bool visible = visible_characters < 0 || p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - line_descent - line_ascent, line_ascent + line_descent); + bool visible = visible_characters < 0 || (p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - line_descent - line_ascent, line_ascent + line_descent)); if (visible) line_is_blank = false; @@ -509,7 +509,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & ENSURE_WIDTH(img->image->get_width()); - bool visible = visible_characters < 0 || p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - font->get_descent() - img->image->get_height(), img->image->get_height()); + bool visible = visible_characters < 0 || (p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - font->get_descent() - img->image->get_height(), img->image->get_height())); if (visible) line_is_blank = false; @@ -847,8 +847,6 @@ void RichTextLabel::_notification(int p_what) { bool use_outline = get_constant("shadow_as_outline"); Point2 shadow_ofs(get_constant("shadow_offset_x"), get_constant("shadow_offset_y")); - float x_ofs = 0; - visible_line_count = 0; while (y < size.height && from_line < main->lines.size()) { @@ -866,7 +864,6 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item if (r_click_item) *r_click_item = NULL; - Size2 size = get_size(); Rect2 text_rect = _get_text_rect(); int ofs = vscroll->get_value(); Color font_color_shadow = get_color("font_color_shadow"); @@ -2237,7 +2234,7 @@ void RichTextLabel::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "meta_underlined"), "set_meta_underline", "is_meta_underlined"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_size", PROPERTY_HINT_RANGE, "0,24,1"), "set_tab_size", "get_tab_size"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_active"), "set_scroll_active", "is_scroll_active"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_following"), "set_scroll_follow", "is_scroll_following"); diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index e3fb602065..26da16569a 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -30,6 +30,7 @@ #include "scroll_container.h" #include "core/os/os.h" + bool ScrollContainer::clips_input() const { return true; @@ -170,7 +171,7 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) { Vector2 motion = Vector2(mm->get_relative().x, mm->get_relative().y); drag_accum -= motion; - if (beyond_deadzone || scroll_h && Math::abs(drag_accum.x) > deadzone || scroll_v && Math::abs(drag_accum.y) > deadzone) { + if (beyond_deadzone || (scroll_h && Math::abs(drag_accum.x) > deadzone) || (scroll_v && Math::abs(drag_accum.y) > deadzone)) { if (!beyond_deadzone) { propagate_notification(NOTIFICATION_SCROLL_BEGIN); emit_signal("scroll_started"); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index a9566d9387..c390c60a8c 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -650,8 +650,6 @@ void TextEdit::_notification(int p_what) { int visible_rows = get_visible_rows() + 1; - int tab_w = cache.font->get_char_size(' ').width * indent_size; - Color color = cache.font_color; color.a *= readonly_alpha; @@ -3796,7 +3794,7 @@ Vector<String> TextEdit::get_wrap_rows_text(int p_line) const { int tab_offset_px = get_indent_level(p_line) * cache.font->get_char_size(' ').width; while (col < line_text.length()) { - char c = line_text[col]; + CharType c = line_text[col]; int w = text.get_char_width(c, line_text[col + 1], px + word_px); int indent_ofs = (cur_wrap_index != 0 ? tab_offset_px : 0); @@ -4402,7 +4400,7 @@ int TextEdit::_is_line_in_region(int p_line) { // if not find the closest line we have int previous_line = p_line - 1; - for (previous_line; previous_line > -1; previous_line--) { + for (; previous_line > -1; previous_line--) { if (color_region_cache.has(p_line)) { break; } @@ -4547,9 +4545,13 @@ void TextEdit::cut() { void TextEdit::copy() { if (!selection.active) { - String clipboard = _base_get_text(cursor.line, 0, cursor.line, text[cursor.line].length()); - OS::get_singleton()->set_clipboard(clipboard); - cut_copy_line = clipboard; + + if (text[cursor.line].length() != 0) { + + String clipboard = _base_get_text(cursor.line, 0, cursor.line, text[cursor.line].length()); + OS::get_singleton()->set_clipboard(clipboard); + cut_copy_line = clipboard; + } } else { String clipboard = _base_get_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); OS::get_singleton()->set_clipboard(clipboard); @@ -5152,7 +5154,7 @@ bool TextEdit::can_fold(int p_line) const { return false; if (p_line + 1 >= text.size()) return false; - if (text[p_line].size() == 0) + if (text[p_line].strip_edges().size() == 0) return false; if (is_folded(p_line)) return false; @@ -5164,7 +5166,7 @@ bool TextEdit::can_fold(int p_line) const { int start_indent = get_indent_level(p_line); for (int i = p_line + 1; i < text.size(); i++) { - if (text[i].size() == 0) + if (text[i].strip_edges().size() == 0) continue; int next_indent = get_indent_level(i); if (is_line_comment(i)) { @@ -5866,7 +5868,7 @@ String TextEdit::get_word_at_pos(const Vector2 &p_pos) const { if (select_word(s, col, beg, end)) { bool inside_quotes = false; - char selected_quote = '\0'; + CharType selected_quote = '\0'; int qbegin = 0, qend = 0; for (int i = 0; i < s.length(); i++) { if (s[i] == '"' || s[i] == '\'') { diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp index 6bd3b26280..413f9dbbe6 100644 --- a/scene/gui/texture_button.cpp +++ b/scene/gui/texture_button.cpp @@ -88,6 +88,9 @@ bool TextureButton::has_point(const Point2 &p_point) const { scale.y = min; ofs -= _texture_region.position / min; } break; + default: { + // FIXME: Why a switch if we only handle one enum value? + } } // offset and scale the new point position to adjust it to the bitmask size @@ -147,6 +150,7 @@ void TextureButton::_notification(int p_what) { } else texdraw = hover; } break; + case DRAW_HOVER_PRESSED: break; // Not used in this class case DRAW_DISABLED: { if (disabled.is_null()) { diff --git a/scene/gui/texture_progress.cpp b/scene/gui/texture_progress.cpp index 8188d1dcf8..d28b4065fb 100644 --- a/scene/gui/texture_progress.cpp +++ b/scene/gui/texture_progress.cpp @@ -229,6 +229,17 @@ void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, F first_section_size = topleft.y; last_section_size = bottomright.y; } break; + case FILL_BILINEAR_LEFT_AND_RIGHT: { + // TODO: Implement + } break; + case FILL_BILINEAR_TOP_AND_BOTTOM: { + // TODO: Implement + } break; + case FILL_CLOCKWISE: + case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE: + case FILL_COUNTER_CLOCKWISE: { + // Those modes are circular, not relevant for nine patch + } break; } double width_filled = width_total * p_ratio; @@ -263,6 +274,17 @@ void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, F dst_rect.size.y = width_filled; topleft.y = last_section_size; } break; + case FILL_BILINEAR_LEFT_AND_RIGHT: { + // TODO: Implement + } break; + case FILL_BILINEAR_TOP_AND_BOTTOM: { + // TODO: Implement + } break; + case FILL_CLOCKWISE: + case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE: + case FILL_COUNTER_CLOCKWISE: { + // Those modes are circular, not relevant for nine patch + } break; } } diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 23e4c26695..3a540d187b 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -1254,13 +1254,13 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 String s = RTR("(Other)"); Vector<String> strings = p_item->cells[i].text.split(","); - for (int i = 0; i < strings.size(); i++) { - int value = i; - if (!strings[i].get_slicec(':', 1).empty()) { - value = strings[i].get_slicec(':', 1).to_int(); + for (int j = 0; j < strings.size(); j++) { + int value = j; + if (!strings[j].get_slicec(':', 1).empty()) { + value = strings[j].get_slicec(':', 1).to_int(); } if (option == value) { - s = strings[i].get_slicec(':', 0); + s = strings[j].get_slicec(':', 0); break; } } @@ -1416,7 +1416,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 while (c) { - if (cache.draw_relationship_lines == 1 && (c->get_parent() != root || c->get_parent() == root && !hide_root)) { + if (cache.draw_relationship_lines == 1 && (c->get_parent() != root || !hide_root)) { int root_ofs = children_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin); int parent_ofs = p_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin); Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - cache.offset + p_draw_ofs; diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 551600109e..34138acb85 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -53,7 +53,6 @@ public: CELL_MODE_STRING, ///< just a string CELL_MODE_CHECK, ///< string + check CELL_MODE_RANGE, ///< Contains a range - CELL_MODE_RANGE_EXPRESSION, ///< Contains a range CELL_MODE_ICON, ///< Contains an icon, not editable CELL_MODE_CUSTOM, ///< Contains a custom value, show a string, and an edit button }; diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp index 17ab234551..39e7c73390 100644 --- a/scene/gui/video_player.cpp +++ b/scene/gui/video_player.cpp @@ -102,6 +102,10 @@ void VideoPlayer::_mix_audio() { } } break; + case AudioServer::SPEAKER_SURROUND_31: { + + // FIXME: Implement + } break; case AudioServer::SPEAKER_SURROUND_51: { AudioFrame *targets[2] = { diff --git a/scene/main/SCsub b/scene/main/SCsub index bf9125be7f..b01e2fd54d 100644 --- a/scene/main/SCsub +++ b/scene/main/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.scene_sources, "*.cpp") - -Export('env') diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index aebc96aad7..fdbe3b57f0 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -1197,6 +1197,9 @@ void SceneTree::_update_root_rect() { } switch (stretch_mode) { + case STRETCH_MODE_DISABLED: { + // Already handled above + } break; case STRETCH_MODE_2D: { root->set_size((screen_size / stretch_shrink).floor()); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 487ca2b009..bb379ff4af 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -701,15 +701,6 @@ void Viewport::set_canvas_transform(const Transform2D &p_transform) { canvas_transform = p_transform; VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, find_world_2d()->get_canvas(), canvas_transform); - - Transform2D xform = (global_canvas_transform * canvas_transform).affine_inverse(); - Size2 ss = get_visible_rect().size; - /*SpatialSound2DServer::get_singleton()->listener_set_transform(internal_listener_2d, Transform2D(0, xform.xform(ss*0.5))); - Vector2 ss2 = ss*xform.get_scale(); - float panrange = MAX(ss2.x,ss2.y); - - SpatialSound2DServer::get_singleton()->listener_set_param(internal_listener_2d, SpatialSound2DServer::LISTENER_PARAM_PAN_RANGE, panrange); -*/ } Transform2D Viewport::get_canvas_transform() const { @@ -722,15 +713,6 @@ void Viewport::_update_global_transform() { Transform2D sxform = stretch_transform * global_canvas_transform; VisualServer::get_singleton()->viewport_set_global_canvas_transform(viewport, sxform); - - Transform2D xform = (sxform * canvas_transform).affine_inverse(); - Size2 ss = get_visible_rect().size; - /*SpatialSound2DServer::get_singleton()->listener_set_transform(internal_listener_2d, Transform2D(0, xform.xform(ss*0.5))); - Vector2 ss2 = ss*xform.get_scale(); - float panrange = MAX(ss2.x,ss2.y); - - SpatialSound2DServer::get_singleton()->listener_set_param(internal_listener_2d, SpatialSound2DServer::LISTENER_PARAM_PAN_RANGE, panrange); -*/ } void Viewport::set_global_canvas_transform(const Transform2D &p_transform) { diff --git a/scene/resources/SCsub b/scene/resources/SCsub index 2ad90247a7..5e5b6f8fd5 100644 --- a/scene/resources/SCsub +++ b/scene/resources/SCsub @@ -4,6 +4,4 @@ Import('env') env.add_source_files(env.scene_sources, "*.cpp") -Export('env') - SConscript("default_theme/SCsub") diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp index 57d0deeb78..9ee85b64b6 100644 --- a/scene/resources/audio_stream_sample.cpp +++ b/scene/resources/audio_stream_sample.cpp @@ -562,13 +562,13 @@ void AudioStreamSample::save_to_wav(String p_path) { PoolVector<uint8_t>::Read read_data = get_data().read(); switch (format) { case AudioStreamSample::FORMAT_8_BITS: - for (int i = 0; i < data_bytes; i++) { + for (unsigned int i = 0; i < data_bytes; i++) { uint8_t data_point = (read_data[i] + 128); file->store_8(data_point); } break; case AudioStreamSample::FORMAT_16_BITS: - for (int i = 0; i < data_bytes / 2; i++) { + for (unsigned int i = 0; i < data_bytes / 2; i++) { uint16_t data_point = decode_uint16(&read_data[i * 2]); file->store_16(data_point); } diff --git a/scene/resources/bit_mask.cpp b/scene/resources/bit_mask.cpp index d670161afd..56b236d03b 100644 --- a/scene/resources/bit_mask.cpp +++ b/scene/resources/bit_mask.cpp @@ -183,7 +183,6 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start) unsigned int count = 0; Set<Point2i> case9s; Set<Point2i> case6s; - int i; Vector<Vector2> _points; do { int sv = 0; diff --git a/scene/resources/default_theme/SCsub b/scene/resources/default_theme/SCsub index bf9125be7f..b01e2fd54d 100644 --- a/scene/resources/default_theme/SCsub +++ b/scene/resources/default_theme/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.scene_sources, "*.cpp") - -Export('env') diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 20fa1d6e2b..4de47b2cb0 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -120,41 +120,7 @@ static Ref<Texture> make_icon(T p_src) { return texture; } -static Ref<Shader> make_shader(const char *vertex_code, const char *fragment_code, const char *lighting_code) { - Ref<Shader> shader = (memnew(Shader())); - //shader->set_code(vertex_code, fragment_code, lighting_code); - - return shader; -} - -static Ref<BitmapFont> make_font(int p_height, int p_ascent, int p_valign, int p_charcount, const int *p_chars, const Ref<Texture> &p_texture) { - - Ref<BitmapFont> font(memnew(BitmapFont)); - font->add_texture(p_texture); - - for (int i = 0; i < p_charcount; i++) { - - const int *c = &p_chars[i * 8]; - - int chr = c[0]; - Rect2 frect; - frect.position.x = c[1]; - frect.position.y = c[2]; - frect.size.x = c[3]; - frect.size.y = c[4]; - Point2 align(c[5], c[6] + p_valign); - int advance = c[7]; - - font->add_char(chr, 0, frect, align, advance); - } - - font->set_height(p_height); - font->set_ascent(p_ascent); - - return font; -} - -static Ref<BitmapFont> make_font2(int p_height, int p_ascent, int p_charcount, const int *p_char_rects, int p_kerning_count, const int *p_kernings, int p_w, int p_h, const unsigned char *p_img) { +static Ref<BitmapFont> make_font(int p_height, int p_ascent, int p_charcount, const int *p_char_rects, int p_kerning_count, const int *p_kernings, int p_w, int p_h, const unsigned char *p_img) { Ref<BitmapFont> font(memnew(BitmapFont)); @@ -209,8 +175,6 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const tex_cache = memnew(TexCacheMap); - //Ref<BitmapFont> default_font = make_font(_bi_font_normal_height,_bi_font_normal_ascent,_bi_font_normal_valign,_bi_font_normal_charcount,_bi_font_normal_characters,make_icon(font_normal_png)); - // Font Colors Color control_font_color = Color::html("e0e0e0"); @@ -364,6 +328,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("pressed", "CheckBox", cbx_empty); theme->set_stylebox("disabled", "CheckBox", cbx_empty); theme->set_stylebox("hover", "CheckBox", cbx_empty); + theme->set_stylebox("hover_pressed", "CheckBox", cbx_empty); theme->set_stylebox("focus", "CheckBox", cbx_focus); theme->set_icon("checked", "CheckBox", make_icon(checked_png)); @@ -376,6 +341,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_color", "CheckBox", control_font_color); theme->set_color("font_color_pressed", "CheckBox", control_font_color_pressed); theme->set_color("font_color_hover", "CheckBox", control_font_color_hover); + theme->set_color("font_color_hover_pressed", "CheckBox", control_font_color_pressed); theme->set_color("font_color_disabled", "CheckBox", control_font_color_disabled); theme->set_constant("hseparation", "CheckBox", 4 * scale); @@ -393,6 +359,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("pressed", "CheckButton", cb_empty); theme->set_stylebox("disabled", "CheckButton", cb_empty); theme->set_stylebox("hover", "CheckButton", cb_empty); + theme->set_stylebox("hover_pressed", "CheckButton", cb_empty); theme->set_stylebox("focus", "CheckButton", focus); theme->set_icon("on", "CheckButton", make_icon(toggle_on_png)); @@ -403,6 +370,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_color", "CheckButton", control_font_color); theme->set_color("font_color_pressed", "CheckButton", control_font_color_pressed); theme->set_color("font_color_hover", "CheckButton", control_font_color_hover); + theme->set_color("font_color_hover_pressed", "CheckButton", control_font_color_pressed); theme->set_color("font_color_disabled", "CheckButton", control_font_color_disabled); theme->set_constant("hseparation", "CheckButton", 4 * scale); @@ -909,9 +877,9 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) { if (p_font.is_valid()) { default_font = p_font; } else if (p_hidpi) { - default_font = make_font2(_hidpi_font_height, _hidpi_font_ascent, _hidpi_font_charcount, &_hidpi_font_charrects[0][0], _hidpi_font_kerning_pair_count, &_hidpi_font_kerning_pairs[0][0], _hidpi_font_img_width, _hidpi_font_img_height, _hidpi_font_img_data); + default_font = make_font(_hidpi_font_height, _hidpi_font_ascent, _hidpi_font_charcount, &_hidpi_font_charrects[0][0], _hidpi_font_kerning_pair_count, &_hidpi_font_kerning_pairs[0][0], _hidpi_font_img_width, _hidpi_font_img_height, _hidpi_font_img_data); } else { - default_font = make_font2(_lodpi_font_height, _lodpi_font_ascent, _lodpi_font_charcount, &_lodpi_font_charrects[0][0], _lodpi_font_kerning_pair_count, &_lodpi_font_kerning_pairs[0][0], _lodpi_font_img_width, _lodpi_font_img_height, _lodpi_font_img_data); + default_font = make_font(_lodpi_font_height, _lodpi_font_ascent, _lodpi_font_charcount, &_lodpi_font_charrects[0][0], _lodpi_font_kerning_pair_count, &_lodpi_font_kerning_pairs[0][0], _lodpi_font_img_width, _lodpi_font_img_height, _lodpi_font_img_data); } Ref<Font> large_font = default_font; fill_default_theme(t, default_font, large_font, default_icon, default_style, p_hidpi ? 2.0 : 1.0); diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index cc8685b952..274c74a9a2 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -427,10 +427,8 @@ void SpatialMaterial::_update_shader() { if (flags[FLAG_USE_VERTEX_LIGHTING]) { code += ",vertex_lighting"; } - bool using_world = false; if (flags[FLAG_TRIPLANAR_USE_WORLD] && (flags[FLAG_UV1_USE_TRIPLANAR] || flags[FLAG_UV2_USE_TRIPLANAR])) { code += ",world_vertex_coords"; - using_world = true; } if (flags[FLAG_DONT_RECEIVE_SHADOWS]) { code += ",shadows_disabled"; @@ -612,11 +610,11 @@ void SpatialMaterial::_update_shader() { code += "\tMODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat_world;\n"; //handle animation - code += "\tint particle_total_frames = particles_anim_h_frames * particles_anim_v_frames;\n"; - code += "\tint particle_frame = int(INSTANCE_CUSTOM.z * float(particle_total_frames));\n"; - code += "\tif (particles_anim_loop) particle_frame=clamp(particle_frame,0,particle_total_frames-1); else particle_frame=abs(particle_frame)%particle_total_frames;\n"; + code += "\tfloat particle_total_frames = float(particles_anim_h_frames * particles_anim_v_frames);\n"; + code += "\tfloat particle_frame = floor(INSTANCE_CUSTOM.z * float(particle_total_frames));\n"; + code += "\tif (particles_anim_loop) particle_frame=clamp(particle_frame,0.0,particle_total_frames-1.0); else particle_frame=mod(particle_frame,float(particle_total_frames));\n"; code += "\tUV /= vec2(float(particles_anim_h_frames),float(particles_anim_v_frames));\n"; - code += "\tUV += vec2(float(particle_frame % particles_anim_h_frames) / float(particles_anim_h_frames),float(particle_frame / particles_anim_h_frames) / float(particles_anim_v_frames));\n"; + code += "\tUV += vec2(mod(particle_frame,float(particles_anim_h_frames)) / float(particles_anim_h_frames),particle_frame / float(particles_anim_h_frames) / float(particles_anim_v_frames));\n"; } break; } @@ -819,7 +817,7 @@ void SpatialMaterial::_update_shader() { code += "\tALPHA = albedo.a * albedo_tex.a;\n"; } - if (proximity_fade_enabled) { + if (!VisualServer::get_singleton()->is_low_end() && proximity_fade_enabled) { code += "\tfloat depth_tex = textureLod(DEPTH_TEXTURE,SCREEN_UV,0.0).r;\n"; code += "\tvec4 world_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV*2.0-1.0,depth_tex*2.0-1.0,1.0);\n"; code += "\tworld_pos.xyz/=world_pos.w;\n"; @@ -827,43 +825,45 @@ void SpatialMaterial::_update_shader() { } if (distance_fade != DISTANCE_FADE_DISABLED) { - if (distance_fade == DISTANCE_FADE_OBJECT_DITHER || distance_fade == DISTANCE_FADE_PIXEL_DITHER) { - - code += "\t{\n"; - if (distance_fade == DISTANCE_FADE_OBJECT_DITHER) { - code += "\t\tfloat fade_distance = abs((INV_CAMERA_MATRIX * WORLD_MATRIX[3]).z);\n"; - - } else { - code += "\t\tfloat fade_distance=-VERTEX.z;\n"; + if ((distance_fade == DISTANCE_FADE_OBJECT_DITHER || distance_fade == DISTANCE_FADE_PIXEL_DITHER)) { + + if (!VisualServer::get_singleton()->is_low_end()) { + code += "\t{\n"; + if (distance_fade == DISTANCE_FADE_OBJECT_DITHER) { + code += "\t\tfloat fade_distance = abs((INV_CAMERA_MATRIX * WORLD_MATRIX[3]).z);\n"; + + } else { + code += "\t\tfloat fade_distance=-VERTEX.z;\n"; + } + + code += "\t\tfloat fade=clamp(smoothstep(distance_fade_min,distance_fade_max,fade_distance),0.0,1.0);\n"; + code += "\t\tint x = int(FRAGCOORD.x) % 4;\n"; + code += "\t\tint y = int(FRAGCOORD.y) % 4;\n"; + code += "\t\tint index = x + y * 4;\n"; + code += "\t\tfloat limit = 0.0;\n\n"; + code += "\t\tif (x < 8) {\n"; + code += "\t\t\tif (index == 0) limit = 0.0625;\n"; + code += "\t\t\tif (index == 1) limit = 0.5625;\n"; + code += "\t\t\tif (index == 2) limit = 0.1875;\n"; + code += "\t\t\tif (index == 3) limit = 0.6875;\n"; + code += "\t\t\tif (index == 4) limit = 0.8125;\n"; + code += "\t\t\tif (index == 5) limit = 0.3125;\n"; + code += "\t\t\tif (index == 6) limit = 0.9375;\n"; + code += "\t\t\tif (index == 7) limit = 0.4375;\n"; + code += "\t\t\tif (index == 8) limit = 0.25;\n"; + code += "\t\t\tif (index == 9) limit = 0.75;\n"; + code += "\t\t\tif (index == 10) limit = 0.125;\n"; + code += "\t\t\tif (index == 11) limit = 0.625;\n"; + code += "\t\t\tif (index == 12) limit = 1.0;\n"; + code += "\t\t\tif (index == 13) limit = 0.5;\n"; + code += "\t\t\tif (index == 14) limit = 0.875;\n"; + code += "\t\t\tif (index == 15) limit = 0.375;\n"; + code += "\t\t}\n\n"; + code += "\tif (fade < limit)\n"; + code += "\t\tdiscard;\n"; + code += "\t}\n\n"; } - code += "\t\tfloat fade=clamp(smoothstep(distance_fade_min,distance_fade_max,fade_distance),0.0,1.0);\n"; - code += "\t\tint x = int(FRAGCOORD.x) % 4;\n"; - code += "\t\tint y = int(FRAGCOORD.y) % 4;\n"; - code += "\t\tint index = x + y * 4;\n"; - code += "\t\tfloat limit = 0.0;\n\n"; - code += "\t\tif (x < 8) {\n"; - code += "\t\t\tif (index == 0) limit = 0.0625;\n"; - code += "\t\t\tif (index == 1) limit = 0.5625;\n"; - code += "\t\t\tif (index == 2) limit = 0.1875;\n"; - code += "\t\t\tif (index == 3) limit = 0.6875;\n"; - code += "\t\t\tif (index == 4) limit = 0.8125;\n"; - code += "\t\t\tif (index == 5) limit = 0.3125;\n"; - code += "\t\t\tif (index == 6) limit = 0.9375;\n"; - code += "\t\t\tif (index == 7) limit = 0.4375;\n"; - code += "\t\t\tif (index == 8) limit = 0.25;\n"; - code += "\t\t\tif (index == 9) limit = 0.75;\n"; - code += "\t\t\tif (index == 10) limit = 0.125;\n"; - code += "\t\t\tif (index == 11) limit = 0.625;\n"; - code += "\t\t\tif (index == 12) limit = 1.0;\n"; - code += "\t\t\tif (index == 13) limit = 0.5;\n"; - code += "\t\t\tif (index == 14) limit = 0.875;\n"; - code += "\t\t\tif (index == 15) limit = 0.375;\n"; - code += "\t\t}\n\n"; - code += "\tif (fade < limit)\n"; - code += "\t\tdiscard;\n"; - code += "\t}\n\n"; - } else { code += "\tALPHA*=clamp(smoothstep(distance_fade_min,distance_fade_max,-VERTEX.z),0.0,1.0);\n"; } @@ -1371,6 +1371,12 @@ void SpatialMaterial::_validate_feature(const String &text, Feature feature, Pro } } +void SpatialMaterial::_validate_high_end(const String &text, PropertyInfo &property) const { + if (property.name.begins_with(text)) { + property.usage |= PROPERTY_USAGE_HIGH_END_GFX; + } +} + void SpatialMaterial::_validate_property(PropertyInfo &property) const { _validate_feature("normal", FEATURE_NORMAL_MAPPING, property); _validate_feature("emission", FEATURE_EMISSION, property); @@ -1384,6 +1390,12 @@ void SpatialMaterial::_validate_property(PropertyInfo &property) const { _validate_feature("refraction", FEATURE_REFRACTION, property); _validate_feature("detail", FEATURE_DETAIL, property); + _validate_high_end("refraction", property); + _validate_high_end("subsurf_scatter", property); + _validate_high_end("anisotropy", property); + _validate_high_end("clearcoat", property); + _validate_high_end("depth", property); + if (property.name.begins_with("particles_anim_") && billboard_mode != BILLBOARD_PARTICLES) { property.usage = 0; } diff --git a/scene/resources/material.h b/scene/resources/material.h index 4a2a813341..0154874ae4 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -442,6 +442,8 @@ private: static Ref<SpatialMaterial> materials_for_2d[MAX_MATERIALS_FOR_2D]; //used by Sprite3D and other stuff + void _validate_high_end(const String &text, PropertyInfo &property) const; + protected: static void _bind_methods(); void _validate_property(PropertyInfo &property) const; diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 6426689397..6cd701eb9a 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -546,46 +546,6 @@ void Mesh::clear_cache() const { Mesh::Mesh() { } -static const char *_array_name[] = { - "vertex_array", - "normal_array", - "tangent_array", - "color_array", - "tex_uv_array", - "tex_uv2_array", - "bone_array", - "weights_array", - "index_array", - NULL -}; - -static const ArrayMesh::ArrayType _array_types[] = { - - ArrayMesh::ARRAY_VERTEX, - ArrayMesh::ARRAY_NORMAL, - ArrayMesh::ARRAY_TANGENT, - ArrayMesh::ARRAY_COLOR, - ArrayMesh::ARRAY_TEX_UV, - ArrayMesh::ARRAY_TEX_UV2, - ArrayMesh::ARRAY_BONES, - ArrayMesh::ARRAY_WEIGHTS, - ArrayMesh::ARRAY_INDEX -}; - -/* compatibility */ -static const int _format_translate[] = { - - ArrayMesh::ARRAY_FORMAT_VERTEX, - ArrayMesh::ARRAY_FORMAT_NORMAL, - ArrayMesh::ARRAY_FORMAT_TANGENT, - ArrayMesh::ARRAY_FORMAT_COLOR, - ArrayMesh::ARRAY_FORMAT_TEX_UV, - ArrayMesh::ARRAY_FORMAT_TEX_UV2, - ArrayMesh::ARRAY_FORMAT_BONES, - ArrayMesh::ARRAY_FORMAT_WEIGHTS, - ArrayMesh::ARRAY_FORMAT_INDEX, -}; - bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) { String sname = p_name; diff --git a/scene/resources/mesh_data_tool.cpp b/scene/resources/mesh_data_tool.cpp index 6732303925..a5449e1fe8 100644 --- a/scene/resources/mesh_data_tool.cpp +++ b/scene/resources/mesh_data_tool.cpp @@ -79,9 +79,9 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf if (arrays[Mesh::ARRAY_COLOR].get_type() != Variant::NIL) col = arrays[Mesh::ARRAY_COLOR].operator PoolVector<Color>().read(); - PoolVector<real_t>::Read bo; + PoolVector<int>::Read bo; if (arrays[Mesh::ARRAY_BONES].get_type() != Variant::NIL) - bo = arrays[Mesh::ARRAY_BONES].operator PoolVector<real_t>().read(); + bo = arrays[Mesh::ARRAY_BONES].operator PoolVector<int>().read(); PoolVector<real_t>::Read we; if (arrays[Mesh::ARRAY_WEIGHTS].get_type() != Variant::NIL) @@ -194,7 +194,7 @@ Error MeshDataTool::commit_to_surface(const Ref<ArrayMesh> &p_mesh) { PoolVector<Vector2> u; PoolVector<Vector2> u2; PoolVector<Color> c; - PoolVector<real_t> b; + PoolVector<int> b; PoolVector<real_t> w; PoolVector<int> in; @@ -233,7 +233,7 @@ Error MeshDataTool::commit_to_surface(const Ref<ArrayMesh> &p_mesh) { col = c.write(); } - PoolVector<real_t>::Write bo; + PoolVector<int>::Write bo; if (format & Mesh::ARRAY_FORMAT_BONES) { b.resize(vcount * 4); bo = b.write(); diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp index 364ec9bb19..6f67ba8af1 100644 --- a/scene/resources/particles_material.cpp +++ b/scene/resources/particles_material.cpp @@ -691,6 +691,7 @@ void ParticlesMaterial::set_param(Parameter p_param, float p_value) { case PARAM_ANIM_OFFSET: { VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset, p_value); } break; + case PARAM_MAX: break; // Can't happen, but silences warning } } float ParticlesMaterial::get_param(Parameter p_param) const { @@ -743,6 +744,7 @@ void ParticlesMaterial::set_param_randomness(Parameter p_param, float p_value) { case PARAM_ANIM_OFFSET: { VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_random, p_value); } break; + case PARAM_MAX: break; // Can't happen, but silences warning } } float ParticlesMaterial::get_param_randomness(Parameter p_param) const { @@ -819,6 +821,7 @@ void ParticlesMaterial::set_param_texture(Parameter p_param, const Ref<Texture> case PARAM_ANIM_OFFSET: { VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_texture, p_texture); } break; + case PARAM_MAX: break; // Can't happen, but silences warning } _queue_shader_change(); diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 16b4ed31df..f0e3979f13 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -207,6 +207,9 @@ void ImageTexture::set_flags(uint32_t p_flags) { flags=p_flags|cube; */ flags = p_flags; + if (w == 0 || h == 0) { + return; //uninitialized, do not set to texture + } VisualServer::get_singleton()->texture_set_flags(texture, p_flags); } diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 74dcd47c48..1802bf12b6 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -80,8 +80,8 @@ public: struct AutotileData { BitmaskMode bitmask_mode; - int spacing; Size2 size; + int spacing; Vector2 icon_coord; Map<Vector2, uint16_t> flags; Map<Vector2, Ref<OccluderPolygon2D> > occluder_map; @@ -90,11 +90,10 @@ public: // Default size to prevent invalid value explicit AutotileData() : + bitmask_mode(BITMASK_2X2), size(64, 64), spacing(0), - icon_coord(0, 0) { - bitmask_mode = BITMASK_2X2; - } + icon_coord(0, 0) {} }; private: @@ -111,8 +110,8 @@ private: Vector2 navigation_polygon_offset; Ref<NavigationPolygon> navigation_polygon; Ref<ShaderMaterial> material; - Color modulate; TileMode tile_mode; + Color modulate; AutotileData autotile_data; int z_index; diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index f12ea8f2bb..96b9cfa137 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -796,10 +796,10 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui val.basis.transpose(); inputs[i] = "n_in" + itos(node) + "p" + itos(i); Array values; - for (int i = 0; i < 3; i++) { - values.push_back(val.basis[i].x); - values.push_back(val.basis[i].y); - values.push_back(val.basis[i].z); + for (int j = 0; j < 3; j++) { + values.push_back(val.basis[j].x); + values.push_back(val.basis[j].y); + values.push_back(val.basis[j].z); } values.push_back(val.origin.x); values.push_back(val.origin.y); diff --git a/scene/resources/world.h b/scene/resources/world.h index 4c517323f3..4ba6b13476 100644 --- a/scene/resources/world.h +++ b/scene/resources/world.h @@ -36,9 +36,9 @@ #include "servers/physics_server.h" #include "servers/visual_server.h" -class SpatialIndexer; class Camera; class VisibilityNotifier; +struct SpatialIndexer; class World : public Resource { GDCLASS(World, Resource); diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp index b390e74073..dd78d04104 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -29,13 +29,13 @@ /*************************************************************************/ #include "world_2d.h" -#include "servers/physics_2d_server.h" -#include "servers/visual_server.h" -//#include "servers/spatial_sound_2d_server.h" + #include "core/project_settings.h" #include "scene/2d/camera_2d.h" #include "scene/2d/visibility_notifier_2d.h" #include "scene/main/viewport.h" +#include "servers/physics_2d_server.h" +#include "servers/visual_server.h" struct SpatialIndexer2D { diff --git a/scene/resources/world_2d.h b/scene/resources/world_2d.h index 856e9dbf01..88ad392f85 100644 --- a/scene/resources/world_2d.h +++ b/scene/resources/world_2d.h @@ -35,9 +35,9 @@ #include "core/resource.h" #include "servers/physics_2d_server.h" -class SpatialIndexer2D; class VisibilityNotifier2D; class Viewport; +struct SpatialIndexer2D; class World2D : public Resource { diff --git a/servers/SCsub b/servers/SCsub index 252a18ffd3..f4af347fe6 100644 --- a/servers/SCsub +++ b/servers/SCsub @@ -5,8 +5,6 @@ Import('env') env.servers_sources = [] env.add_source_files(env.servers_sources, "*.cpp") -Export('env') - SConscript('arvr/SCsub') SConscript('physics/SCsub') SConscript('physics_2d/SCsub') diff --git a/servers/arvr/SCsub b/servers/arvr/SCsub index ccc76e823f..d730144861 100644 --- a/servers/arvr/SCsub +++ b/servers/arvr/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.servers_sources, "*.cpp") - -Export('env') diff --git a/servers/audio/SCsub b/servers/audio/SCsub index afaffcfe93..3c18c18043 100644 --- a/servers/audio/SCsub +++ b/servers/audio/SCsub @@ -4,6 +4,4 @@ Import('env') env.add_source_files(env.servers_sources, "*.cpp") -Export('env') - SConscript("effects/SCsub") diff --git a/servers/audio/audio_rb_resampler.cpp b/servers/audio/audio_rb_resampler.cpp index 3ae897c299..84a87de2e2 100644 --- a/servers/audio/audio_rb_resampler.cpp +++ b/servers/audio/audio_rb_resampler.cpp @@ -82,19 +82,28 @@ uint32_t AudioRBResampler::_resample(AudioFrame *p_dest, int p_todo, int32_t p_i // For now, channels higher than stereo are almost ignored if (C == 4) { + // FIXME: v2 and v3 are not being used (thus were commented out to prevent + // compilation warnings, but they should likely be uncommented *and* used). + // See also C == 6 with similar issues. float v0 = rb[(pos << 2) + 0]; float v1 = rb[(pos << 2) + 1]; + /* float v2 = rb[(pos << 2) + 2]; float v3 = rb[(pos << 2) + 3]; + */ float v0n = rb[(pos_next << 2) + 0]; float v1n = rb[(pos_next << 2) + 1]; + /* float v2n = rb[(pos_next << 2) + 2]; float v3n = rb[(pos_next << 2) + 3]; + */ v0 += (v0n - v0) * frac; v1 += (v1n - v1) * frac; + /* v2 += (v2n - v2) * frac; v3 += (v3n - v3) * frac; + */ p_dest[i] = AudioFrame(v0, v1); } @@ -104,6 +113,7 @@ uint32_t AudioRBResampler::_resample(AudioFrame *p_dest, int p_todo, int32_t p_i // should be done as for C == 2 (C == 4 also has some unused assignments). float v0 = rb[(pos * 6) + 0]; float v1 = rb[(pos * 6) + 1]; + /* float v2 = rb[(pos * 6) + 2]; float v3 = rb[(pos * 6) + 3]; float v4 = rb[(pos * 6) + 4]; @@ -114,6 +124,7 @@ uint32_t AudioRBResampler::_resample(AudioFrame *p_dest, int p_todo, int32_t p_i float v3n = rb[(pos_next * 6) + 3]; float v4n = rb[(pos_next * 6) + 4]; float v5n = rb[(pos_next * 6) + 5]; + */ p_dest[i] = AudioFrame(v0, v1); } @@ -153,7 +164,7 @@ bool AudioRBResampler::mix(AudioFrame *p_dest, int p_frames) { } // Fill zeros (silence) for the rest of frames - for (uint32_t i = target_todo; i < p_frames; i++) { + for (int i = target_todo; i < p_frames; i++) { p_dest[i] = AudioFrame(0, 0); } } diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h index c7763890fc..2740f86d55 100644 --- a/servers/audio/audio_stream.h +++ b/servers/audio/audio_stream.h @@ -122,7 +122,7 @@ class AudioStreamPlaybackMicrophone : public AudioStreamPlaybackResampled { GDCLASS(AudioStreamPlaybackMicrophone, AudioStreamPlayback) friend class AudioStreamMicrophone; - const int MICROPHONE_PLAYBACK_DELAY = 256; + static const int MICROPHONE_PLAYBACK_DELAY = 256; bool active; unsigned int input_ofs; diff --git a/servers/audio/effects/SCsub b/servers/audio/effects/SCsub index ccc76e823f..d730144861 100644 --- a/servers/audio/effects/SCsub +++ b/servers/audio/effects/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.servers_sources, "*.cpp") - -Export('env') diff --git a/servers/audio/effects/audio_effect_record.h b/servers/audio/effects/audio_effect_record.h index 5f5c7802b4..edf8565c06 100644 --- a/servers/audio/effects/audio_effect_record.h +++ b/servers/audio/effects/audio_effect_record.h @@ -49,7 +49,7 @@ class AudioEffectRecordInstance : public AudioEffectInstance { bool is_recording; Thread *io_thread; - bool thread_active = false; + bool thread_active; Vector<AudioFrame> ring_buffer; Vector<float> recording_data; @@ -67,6 +67,9 @@ public: void init(); virtual void process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count); virtual bool process_silence(); + + AudioEffectRecordInstance() : + thread_active(false) {} }; class AudioEffectRecord : public AudioEffect { diff --git a/servers/audio/reverb_sw.cpp b/servers/audio/reverb_sw.cpp index 13742d5db5..d078da38b4 100644 --- a/servers/audio/reverb_sw.cpp +++ b/servers/audio/reverb_sw.cpp @@ -29,33 +29,21 @@ /*************************************************************************/ #include "reverb_sw.h" + #include "core/print_string.h" -#include "stdlib.h" + +#include <stdlib.h> + #define SETMIN(x, y) (x) = MIN((x), (y)) + #define rangeloop(c, min, max) \ for ((c) = (min); (c) < (max); (c)++) #define ABSDIFF(x, y) \ (((x) < (y)) ? ((y) - (x)) : ((x) - (y))) -#ifdef bleh_MSC_VER - -#if _MSC_VER >= 1400 -_FORCE_INLINE_ int32_tMULSHIFT_S32( - int32_t Factor1, - int32_t Factor2, - uint8_t Bits) { - - return __ll_rshift( - __emul(Factor1, Factor2), - Bits); -} -#endif - -#else #define MULSHIFT_S32(Factor1, Factor2, Bits) \ ((int)(((int64_t)(Factor1) * (Factor2)) >> (Bits))) -#endif struct ReverbParamsSW { unsigned int BufferSize; // Required buffer size diff --git a/servers/audio/reverb_sw.h b/servers/audio/reverb_sw.h index 26b3fa5166..01977d1f61 100644 --- a/servers/audio/reverb_sw.h +++ b/servers/audio/reverb_sw.h @@ -34,7 +34,7 @@ #include "core/os/memory.h" #include "core/typedefs.h" -class ReverbParamsSW; +struct ReverbParamsSW; class ReverbSW { public: diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index 070099a3d8..fead2f54da 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -189,7 +189,7 @@ void AudioDriverManager::initialize(int p_driver) { } } - if (driver_count > 1 && AudioDriver::get_singleton()->get_name() == "Dummy") { + if (driver_count > 1 && String(AudioDriver::get_singleton()->get_name()) == "Dummy") { WARN_PRINT("All audio drivers failed, falling back to the dummy driver."); } } diff --git a/servers/physics/SCsub b/servers/physics/SCsub index c0ee2cc739..c5cc889112 100644 --- a/servers/physics/SCsub +++ b/servers/physics/SCsub @@ -4,6 +4,4 @@ Import('env') env.add_source_files(env.servers_sources, "*.cpp") -Export('env') - SConscript("joints/SCsub") diff --git a/servers/physics/body_pair_sw.h b/servers/physics/body_pair_sw.h index fd85d77718..17ff9d6a88 100644 --- a/servers/physics/body_pair_sw.h +++ b/servers/physics/body_pair_sw.h @@ -76,7 +76,6 @@ class BodyPairSW : public ConstraintSW { Contact contacts[MAX_CONTACTS]; int contact_count; bool collided; - int cc; static void _contact_added_callback(const Vector3 &p_point_A, const Vector3 &p_point_B, void *p_userdata); diff --git a/servers/physics/body_sw.cpp b/servers/physics/body_sw.cpp index cc9681193c..36511f78ce 100644 --- a/servers/physics/body_sw.cpp +++ b/servers/physics/body_sw.cpp @@ -755,10 +755,10 @@ void BodySW::set_kinematic_margin(real_t p_margin) { BodySW::BodySW() : CollisionObjectSW(TYPE_BODY), + locked_axis(0), active_list(this), inertia_update_list(this), - direct_state_query_list(this), - locked_axis(0) { + direct_state_query_list(this) { mode = PhysicsServer::BODY_MODE_RIGID; active = true; diff --git a/servers/physics/joints/SCsub b/servers/physics/joints/SCsub index ccc76e823f..d730144861 100644 --- a/servers/physics/joints/SCsub +++ b/servers/physics/joints/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.servers_sources, "*.cpp") - -Export('env') diff --git a/servers/physics/joints/cone_twist_joint_sw.cpp b/servers/physics/joints/cone_twist_joint_sw.cpp index c06f27cc57..37fcde4b76 100644 --- a/servers/physics/joints/cone_twist_joint_sw.cpp +++ b/servers/physics/joints/cone_twist_joint_sw.cpp @@ -332,6 +332,7 @@ void ConeTwistJointSW::set_param(PhysicsServer::ConeTwistJointParam p_param, rea m_relaxationFactor = p_value; } break; + case PhysicsServer::CONE_TWIST_MAX: break; // Can't happen, but silences warning } } @@ -358,6 +359,7 @@ real_t ConeTwistJointSW::get_param(PhysicsServer::ConeTwistJointParam p_param) c return m_relaxationFactor; } break; + case PhysicsServer::CONE_TWIST_MAX: break; // Can't happen, but silences warning } return 0; diff --git a/servers/physics/joints/generic_6dof_joint_sw.cpp b/servers/physics/joints/generic_6dof_joint_sw.cpp index c95e5cef32..9b1a41e80d 100644 --- a/servers/physics/joints/generic_6dof_joint_sw.cpp +++ b/servers/physics/joints/generic_6dof_joint_sw.cpp @@ -497,6 +497,13 @@ void Generic6DOFJointSW::set_param(Vector3::Axis p_axis, PhysicsServer::G6DOFJoi m_angularLimits[p_axis].m_maxLimitForce = p_value; } break; + case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY: { + // Not implemented in GodotPhysics backend + } break; + case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT: { + // Not implemented in GodotPhysics backend + } break; + case PhysicsServer::G6DOF_JOINT_MAX: break; // Can't happen, but silences warning } } @@ -572,6 +579,13 @@ real_t Generic6DOFJointSW::get_param(Vector3::Axis p_axis, PhysicsServer::G6DOFJ return m_angularLimits[p_axis].m_maxMotorForce; } break; + case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY: { + // Not implemented in GodotPhysics backend + } break; + case PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT: { + // Not implemented in GodotPhysics backend + } break; + case PhysicsServer::G6DOF_JOINT_MAX: break; // Can't happen, but silences warning } return 0; } @@ -593,6 +607,10 @@ void Generic6DOFJointSW::set_flag(Vector3::Axis p_axis, PhysicsServer::G6DOFJoin m_angularLimits[p_axis].m_enableMotor = p_value; } break; + case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR: { + // Not implemented in GodotPhysics backend + } break; + case PhysicsServer::G6DOF_JOINT_FLAG_MAX: break; // Can't happen, but silences warning } } bool Generic6DOFJointSW::get_flag(Vector3::Axis p_axis, PhysicsServer::G6DOFJointAxisFlag p_flag) const { @@ -611,6 +629,10 @@ bool Generic6DOFJointSW::get_flag(Vector3::Axis p_axis, PhysicsServer::G6DOFJoin return m_angularLimits[p_axis].m_enableMotor; } break; + case PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR: { + // Not implemented in GodotPhysics backend + } break; + case PhysicsServer::G6DOF_JOINT_FLAG_MAX: break; // Can't happen, but silences warning } return 0; diff --git a/servers/physics/joints/hinge_joint_sw.cpp b/servers/physics/joints/hinge_joint_sw.cpp index 368a349632..50de0e871e 100644 --- a/servers/physics/joints/hinge_joint_sw.cpp +++ b/servers/physics/joints/hinge_joint_sw.cpp @@ -409,6 +409,7 @@ void HingeJointSW::set_param(PhysicsServer::HingeJointParam p_param, real_t p_va case PhysicsServer::HINGE_JOINT_LIMIT_RELAXATION: m_relaxationFactor = p_value; break; case PhysicsServer::HINGE_JOINT_MOTOR_TARGET_VELOCITY: m_motorTargetVelocity = p_value; break; case PhysicsServer::HINGE_JOINT_MOTOR_MAX_IMPULSE: m_maxMotorImpulse = p_value; break; + case PhysicsServer::HINGE_JOINT_MAX: break; // Can't happen, but silences warning } } @@ -424,6 +425,7 @@ real_t HingeJointSW::get_param(PhysicsServer::HingeJointParam p_param) const { case PhysicsServer::HINGE_JOINT_LIMIT_RELAXATION: return m_relaxationFactor; case PhysicsServer::HINGE_JOINT_MOTOR_TARGET_VELOCITY: return m_motorTargetVelocity; case PhysicsServer::HINGE_JOINT_MOTOR_MAX_IMPULSE: return m_maxMotorImpulse; + case PhysicsServer::HINGE_JOINT_MAX: break; // Can't happen, but silences warning } return 0; @@ -434,6 +436,7 @@ void HingeJointSW::set_flag(PhysicsServer::HingeJointFlag p_flag, bool p_value) switch (p_flag) { case PhysicsServer::HINGE_JOINT_FLAG_USE_LIMIT: m_useLimit = p_value; break; case PhysicsServer::HINGE_JOINT_FLAG_ENABLE_MOTOR: m_enableAngularMotor = p_value; break; + case PhysicsServer::HINGE_JOINT_FLAG_MAX: break; // Can't happen, but silences warning } } bool HingeJointSW::get_flag(PhysicsServer::HingeJointFlag p_flag) const { @@ -441,6 +444,7 @@ bool HingeJointSW::get_flag(PhysicsServer::HingeJointFlag p_flag) const { switch (p_flag) { case PhysicsServer::HINGE_JOINT_FLAG_USE_LIMIT: return m_useLimit; case PhysicsServer::HINGE_JOINT_FLAG_ENABLE_MOTOR: return m_enableAngularMotor; + case PhysicsServer::HINGE_JOINT_FLAG_MAX: break; // Can't happen, but silences warning } return false; diff --git a/servers/physics/joints/slider_joint_sw.cpp b/servers/physics/joints/slider_joint_sw.cpp index c0e9660b22..30700d45f1 100644 --- a/servers/physics/joints/slider_joint_sw.cpp +++ b/servers/physics/joints/slider_joint_sw.cpp @@ -404,6 +404,8 @@ void SliderJointSW::set_param(PhysicsServer::SliderJointParam p_param, real_t p_ case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_SOFTNESS: m_softnessOrthoAng = p_value; break; case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_RESTITUTION: m_restitutionOrthoAng = p_value; break; case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_DAMPING: m_dampingOrthoAng = p_value; break; + + case PhysicsServer::SLIDER_JOINT_MAX: break; // Can't happen, but silences warning } } @@ -433,6 +435,8 @@ real_t SliderJointSW::get_param(PhysicsServer::SliderJointParam p_param) const { case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_SOFTNESS: return m_softnessOrthoAng; case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_RESTITUTION: return m_restitutionOrthoAng; case PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_DAMPING: return m_dampingOrthoAng; + + case PhysicsServer::SLIDER_JOINT_MAX: break; // Can't happen, but silences warning } return 0; diff --git a/servers/physics_2d/collision_object_2d_sw.h b/servers/physics_2d/collision_object_2d_sw.h index c3b9e4b713..f2b2363499 100644 --- a/servers/physics_2d/collision_object_2d_sw.h +++ b/servers/physics_2d/collision_object_2d_sw.h @@ -112,23 +112,23 @@ public: _FORCE_INLINE_ int get_shape_count() const { return shapes.size(); } _FORCE_INLINE_ Shape2DSW *get_shape(int p_index) const { - ERR_FAIL_INDEX_V(p_index, shapes.size(), NULL); + CRASH_BAD_INDEX(p_index, shapes.size()); return shapes[p_index].shape; } _FORCE_INLINE_ const Transform2D &get_shape_transform(int p_index) const { - ERR_FAIL_INDEX_V(p_index, shapes.size(), Transform2D()); + CRASH_BAD_INDEX(p_index, shapes.size()); return shapes[p_index].xform; } _FORCE_INLINE_ const Transform2D &get_shape_inv_transform(int p_index) const { - ERR_FAIL_INDEX_V(p_index, shapes.size(), Transform2D()); + CRASH_BAD_INDEX(p_index, shapes.size()); return shapes[p_index].xform_inv; } _FORCE_INLINE_ const Rect2 &get_shape_aabb(int p_index) const { - ERR_FAIL_INDEX_V(p_index, shapes.size(), Rect2()); + CRASH_BAD_INDEX(p_index, shapes.size()); return shapes[p_index].aabb_cache; } _FORCE_INLINE_ const Variant &get_shape_metadata(int p_index) const { - ERR_FAIL_INDEX_V(p_index, shapes.size(), Variant()); + CRASH_BAD_INDEX(p_index, shapes.size()); return shapes[p_index].metadata; } @@ -138,16 +138,16 @@ public: void set_shape_as_disabled(int p_idx, bool p_disabled); _FORCE_INLINE_ bool is_shape_set_as_disabled(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, shapes.size(), false); + CRASH_BAD_INDEX(p_idx, shapes.size()); return shapes[p_idx].disabled; } _FORCE_INLINE_ void set_shape_as_one_way_collision(int p_idx, bool p_one_way_collision) { - ERR_FAIL_INDEX(p_idx, shapes.size()); + CRASH_BAD_INDEX(p_idx, shapes.size()); shapes.write[p_idx].one_way_collision = p_one_way_collision; } _FORCE_INLINE_ bool is_shape_set_as_one_way_collision(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, shapes.size(), false); + CRASH_BAD_INDEX(p_idx, shapes.size()); return shapes[p_idx].one_way_collision; } diff --git a/servers/physics_2d_server.h b/servers/physics_2d_server.h index fc2a228723..7c23fcdaea 100644 --- a/servers/physics_2d_server.h +++ b/servers/physics_2d_server.h @@ -372,7 +372,6 @@ public: BODY_MODE_KINEMATIC, BODY_MODE_RIGID, BODY_MODE_CHARACTER - //BODY_MODE_SOFT ?? }; virtual RID body_create() = 0; @@ -581,9 +580,7 @@ public: INFO_ACTIVE_OBJECTS, INFO_COLLISION_PAIRS, - INFO_ISLAND_COUNT, - INFO_STEP_TIME, - INFO_BROAD_PHASE_TIME + INFO_ISLAND_COUNT }; virtual int get_process_info(ProcessInfo p_info) = 0; diff --git a/servers/physics_server.cpp b/servers/physics_server.cpp index 0660c84d09..888e16e0c3 100644 --- a/servers/physics_server.cpp +++ b/servers/physics_server.cpp @@ -678,7 +678,6 @@ void PhysicsServer::_bind_methods() { BIND_ENUM_CONSTANT(BODY_MODE_STATIC); BIND_ENUM_CONSTANT(BODY_MODE_KINEMATIC); BIND_ENUM_CONSTANT(BODY_MODE_RIGID); - BIND_ENUM_CONSTANT(BODY_MODE_SOFT); BIND_ENUM_CONSTANT(BODY_MODE_CHARACTER); BIND_ENUM_CONSTANT(BODY_PARAM_BOUNCE); diff --git a/servers/physics_server.h b/servers/physics_server.h index d0d2ec16f0..d80d76305a 100644 --- a/servers/physics_server.h +++ b/servers/physics_server.h @@ -360,7 +360,6 @@ public: BODY_MODE_STATIC, BODY_MODE_KINEMATIC, BODY_MODE_RIGID, - BODY_MODE_SOFT, BODY_MODE_CHARACTER }; diff --git a/servers/visual/SCsub b/servers/visual/SCsub index ccc76e823f..d730144861 100644 --- a/servers/visual/SCsub +++ b/servers/visual/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.servers_sources, "*.cpp") - -Export('env') diff --git a/servers/visual/default_mouse_cursor.xpm b/servers/visual/default_mouse_cursor.xpm deleted file mode 100644 index 37d437dd15..0000000000 --- a/servers/visual/default_mouse_cursor.xpm +++ /dev/null @@ -1,23 +0,0 @@ -/* XPM */ -static const char * default_mouse_cursor_xpm[] = { -"16 16 4 1", -" c None", -". c #000000", -"+ c #FF00FF", -"@ c #FFFFFF", -"...+++++++++++++", -".@...+++++++++++", -".@@@...+++++++++", -".@@@@@....++++++", -".@@@@@@@@...++++", -".@@@@@@@@@@...++", -".@@@@@@@@@@@@..+", -".@@@@@@@@@@@@@..", -".@@@@@@@@@@@@..+", -".@@@@@@@@@@@..++", -".@@@@@@@@@...+++", -".@@@.....@@..+++", -".....+++.@@@..++", -"++++++++..@@@..+", -"+++++++++..@@@.+", -"++++++++++.....+"}; diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h index 6eeaf12dfc..207f13ac1a 100644 --- a/servers/visual/rasterizer.h +++ b/servers/visual/rasterizer.h @@ -397,6 +397,7 @@ public: virtual RID reflection_probe_create() = 0; virtual void reflection_probe_set_update_mode(RID p_probe, VS::ReflectionProbeUpdateMode p_mode) = 0; + virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution) = 0; virtual void reflection_probe_set_intensity(RID p_probe, float p_intensity) = 0; virtual void reflection_probe_set_interior_ambient(RID p_probe, const Color &p_ambient) = 0; virtual void reflection_probe_set_interior_ambient_energy(RID p_probe, float p_energy) = 0; @@ -1106,6 +1107,8 @@ public: virtual void end_frame(bool p_swap_buffers) = 0; virtual void finalize() = 0; + virtual bool is_low_end() const = 0; + virtual ~Rasterizer() {} }; diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index 417617fa5b..67a810bf1c 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -2023,10 +2023,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, OperatorNode *p StringName name = static_cast<VariableNode *>(p_func->arguments[0])->name.operator String(); - bool all_const = true; for (int i = 1; i < p_func->arguments.size(); i++) { - if (p_func->arguments[i]->type != Node::TYPE_CONSTANT) - all_const = false; args.push_back(p_func->arguments[i]->get_datatype()); } @@ -2369,6 +2366,21 @@ Variant ShaderLanguage::constant_value_to_variant(const Vector<ShaderLanguage::C value = Variant(t); break; } + case ShaderLanguage::TYPE_ISAMPLER2DARRAY: + case ShaderLanguage::TYPE_ISAMPLER2D: + case ShaderLanguage::TYPE_ISAMPLER3D: + case ShaderLanguage::TYPE_SAMPLER2DARRAY: + case ShaderLanguage::TYPE_SAMPLER2D: + case ShaderLanguage::TYPE_SAMPLER3D: + case ShaderLanguage::TYPE_USAMPLER2DARRAY: + case ShaderLanguage::TYPE_USAMPLER2D: + case ShaderLanguage::TYPE_USAMPLER3D: + case ShaderLanguage::TYPE_SAMPLERCUBE: { + // Texture types, likely not relevant here. + break; + } + case ShaderLanguage::TYPE_VOID: + break; } return value; } @@ -3008,6 +3020,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons case TYPE_IVEC2: member_type = TYPE_INT; break; case TYPE_UVEC2: member_type = TYPE_UINT; break; case TYPE_MAT2: member_type = TYPE_VEC2; break; + default: break; } break; @@ -3033,6 +3046,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons case TYPE_IVEC3: member_type = TYPE_INT; break; case TYPE_UVEC3: member_type = TYPE_UINT; break; case TYPE_MAT3: member_type = TYPE_VEC3; break; + default: break; } break; case TYPE_BVEC4: @@ -3057,6 +3071,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons case TYPE_IVEC4: member_type = TYPE_INT; break; case TYPE_UVEC4: member_type = TYPE_UINT; break; case TYPE_MAT4: member_type = TYPE_VEC4; break; + default: break; } break; default: { diff --git a/servers/visual/visual_server_raster.cpp b/servers/visual/visual_server_raster.cpp index ea63ae5013..677c323216 100644 --- a/servers/visual/visual_server_raster.cpp +++ b/servers/visual/visual_server_raster.cpp @@ -34,7 +34,6 @@ #include "core/os/os.h" #include "core/project_settings.h" #include "core/sort.h" -#include "default_mouse_cursor.xpm" #include "visual_server_canvas.h" #include "visual_server_global.h" #include "visual_server_scene.h" @@ -190,6 +189,9 @@ void VisualServerRaster::call_set_use_vsync(bool p_enable) { OS::get_singleton()->_set_use_vsync(p_enable); } +bool VisualServerRaster::is_low_end() const { + return VSG::rasterizer->is_low_end(); +} VisualServerRaster::VisualServerRaster() { VSG::canvas = memnew(VisualServerCanvas); diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h index b54e150656..62ba2eab69 100644 --- a/servers/visual/visual_server_raster.h +++ b/servers/visual/visual_server_raster.h @@ -59,7 +59,6 @@ class VisualServerRaster : public VisualServer { }; static int changes; - bool draw_extra_frame; RID test_cube; int black_margin[4]; @@ -337,6 +336,7 @@ public: BIND2(reflection_probe_set_enable_box_projection, RID, bool) BIND2(reflection_probe_set_enable_shadows, RID, bool) BIND2(reflection_probe_set_cull_mask, RID, uint32_t) + BIND2(reflection_probe_set_resolution, RID, int) /* BAKED LIGHT API */ @@ -689,6 +689,8 @@ public: virtual void call_set_use_vsync(bool p_enable); + virtual bool is_low_end() const; + VisualServerRaster(); ~VisualServerRaster(); diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp index eacb5f671c..cd0702d20b 100644 --- a/servers/visual/visual_server_scene.cpp +++ b/servers/visual/visual_server_scene.cpp @@ -398,6 +398,7 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base) { VSG::scene_render->free(gi_probe->probe_instance); } break; + default: {} } if (instance->base_data) { @@ -471,6 +472,7 @@ void VisualServerScene::instance_set_base(RID p_instance, RID p_base) { gi_probe->probe_instance = VSG::scene_render->gi_probe_instance_create(); } break; + default: {} } VSG::storage->instance_add_dependency(p_base, instance); @@ -518,6 +520,7 @@ void VisualServerScene::instance_set_scenario(RID p_instance, RID p_scenario) { gi_probe_update_list.remove(&gi_probe->update_element); } } break; + default: {} } instance->scenario = NULL; @@ -549,6 +552,7 @@ void VisualServerScene::instance_set_scenario(RID p_instance, RID p_scenario) { gi_probe_update_list.add(&gi_probe->update_element); } } break; + default: {} } _instance_queue_update(instance, true, true); @@ -649,6 +653,7 @@ void VisualServerScene::instance_set_visible(RID p_instance, bool p_visible) { } } break; + default: {} } } inline bool is_geometry_instance(VisualServer::InstanceType p_type) { @@ -825,6 +830,7 @@ void VisualServerScene::instance_geometry_set_flag(RID p_instance, VS::InstanceF instance->redraw_if_visible = p_enabled; } break; + default: {} } } void VisualServerScene::instance_geometry_set_cast_shadows_setting(RID p_instance, VS::ShadowCastingSetting p_shadow_casting_setting) { @@ -902,7 +908,7 @@ void VisualServerScene::_update_instance(Instance *p_instance) { _update_instance_lightmap_captures(p_instance); } else { if (!p_instance->lightmap_capture_data.empty()) { - !p_instance->lightmap_capture_data.resize(0); //not in use, clear capture data + p_instance->lightmap_capture_data.resize(0); //not in use, clear capture data } } } @@ -1016,7 +1022,6 @@ void VisualServerScene::_update_instance_aabb(Instance *p_instance) { new_aabb = VSG::storage->lightmap_capture_get_bounds(p_instance->base); } break; - default: {} } @@ -1378,9 +1383,12 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons float y_min = 0.f, y_max = 0.f; float z_min = 0.f, z_max = 0.f; + // FIXME: z_max_cam is defined, computed, but not used below when setting up + // ortho_camera. Commented out for now to fix warnings but should be investigated. float x_min_cam = 0.f, x_max_cam = 0.f; float y_min_cam = 0.f, y_max_cam = 0.f; - float z_min_cam = 0.f, z_max_cam = 0.f; + float z_min_cam = 0.f; + //float z_max_cam = 0.f; float bias_scale = 1.0; @@ -1442,7 +1450,7 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons x_min_cam = x_vec.dot(center) - radius; y_max_cam = y_vec.dot(center) + radius; y_min_cam = y_vec.dot(center) - radius; - z_max_cam = z_vec.dot(center) + radius; + //z_max_cam = z_vec.dot(center) + radius; z_min_cam = z_vec.dot(center) - radius; if (depth_range_mode == VS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE) { @@ -2133,6 +2141,8 @@ bool VisualServerScene::_render_reflection_probe_step(Instance *p_instance, int Scenario *scenario = p_instance->scenario; ERR_FAIL_COND_V(!scenario, true); + VisualServerRaster::redraw_request(); //update, so it updates in editor + if (p_step == 0) { if (!VSG::scene_render->reflection_probe_instance_begin_render(reflection_probe->instance, scenario->reflection_atlas)) { diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h index b8f86d7123..e4d69121f0 100644 --- a/servers/visual/visual_server_wrap_mt.h +++ b/servers/visual/visual_server_wrap_mt.h @@ -271,6 +271,7 @@ public: FUNC2(reflection_probe_set_enable_box_projection, RID, bool) FUNC2(reflection_probe_set_enable_shadows, RID, bool) FUNC2(reflection_probe_set_cull_mask, RID, uint32_t) + FUNC2(reflection_probe_set_resolution, RID, int) /* BAKED LIGHT API */ @@ -608,6 +609,10 @@ public: static void set_use_vsync_callback(bool p_enable); + virtual bool is_low_end() const { + return visual_server->is_low_end(); + } + VisualServerWrapMT(VisualServer *p_contained, bool p_create_thread); ~VisualServerWrapMT(); diff --git a/servers/visual_server.h b/servers/visual_server.h index 6a1f2c3550..100bc06db6 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -490,6 +490,7 @@ public: virtual void reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) = 0; virtual void reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) = 0; virtual void reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) = 0; + virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution) = 0; /* GI PROBE API */ @@ -1036,6 +1037,8 @@ public: virtual void call_set_use_vsync(bool p_enable) = 0; + virtual bool is_low_end() const = 0; + VisualServer(); virtual ~VisualServer(); }; diff --git a/thirdparty/README.md b/thirdparty/README.md index 71053de016..7fdd9e20b0 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -375,6 +375,10 @@ Collection of single-file libraries used in Godot components. ### scene +- `easing_equations.cpp` + * Upstream: http://robertpenner.com/easing/ via https://github.com/jesusgollonet/ofpennereasing (modified to fit Godot types) + * Version: git (af72c14, 2008) + Godot types and style changes + * License: BSD-3-Clause - `mikktspace.{c,h}` * Upstream: https://wiki.blender.org/index.php/Dev:Shading/Tangent_Space_Normal_Maps * Version: 1.0 diff --git a/scene/animation/tween_interpolaters.cpp b/thirdparty/misc/easing_equations.cpp index 52aa7403c0..bc84564b19 100644 --- a/scene/animation/tween_interpolaters.cpp +++ b/thirdparty/misc/easing_equations.cpp @@ -1,40 +1,10 @@ -/*************************************************************************/ -/* tween_interpolaters.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. */ -/*************************************************************************/ - /** * Adapted from Penner Easing equations' C++ port. * Source: https://github.com/jesusgollonet/ofpennereasing * License: BSD-3-clause */ -#include "tween.h" +#include "scene/animation/tween.h" const real_t pi = 3.1415926535898; diff --git a/thirdparty/misc/stb_vorbis.h b/thirdparty/misc/stb_vorbis.h new file mode 100644 index 0000000000..357efcd5fc --- /dev/null +++ b/thirdparty/misc/stb_vorbis.h @@ -0,0 +1,2 @@ +#define STB_VORBIS_HEADER_ONLY +#include "stb_vorbis.c" diff --git a/thirdparty/xatlas/xatlas.cpp b/thirdparty/xatlas/xatlas.cpp new file mode 100644 index 0000000000..f6a9ce64dc --- /dev/null +++ b/thirdparty/xatlas/xatlas.cpp @@ -0,0 +1,7384 @@ +// This code is in the public domain -- castanyo@yahoo.es +#include "xatlas.h" +#include <assert.h> +#include <float.h> +#include <math.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <algorithm> +#include <cmath> +#include <memory> +#include <unordered_map> +#include <vector> + +#undef min +#undef max + +#ifndef xaAssert +#define xaAssert(exp) \ + if (!(exp)) { \ + xaPrint("%s %s %s\n", #exp, __FILE__, __LINE__); \ + } +#endif +#ifndef xaDebugAssert +#define xaDebugAssert(exp) assert(exp) +#endif +#ifndef xaPrint +#define xaPrint(...) \ + if (xatlas::internal::s_print) { \ + xatlas::internal::s_print(__VA_ARGS__); \ + } +#endif + +#ifdef _MSC_VER +// Ignore gcc attributes. +#define __attribute__(X) +#endif + +#ifdef _MSC_VER +#define restrict +#define NV_FORCEINLINE __forceinline +#else +#define restrict __restrict__ +#define NV_FORCEINLINE __attribute__((always_inline)) inline +#endif + +#define NV_UINT32_MAX 0xffffffff +#define NV_FLOAT_MAX 3.402823466e+38F + +#ifndef PI +#define PI float(3.1415926535897932384626433833) +#endif + +#define NV_EPSILON (0.0001f) +#define NV_NORMAL_EPSILON (0.001f) + +namespace xatlas { +namespace internal { + +static PrintFunc s_print = NULL; + +static int align(int x, int a) { + return (x + a - 1) & ~(a - 1); +} + +static bool isAligned(int x, int a) { + return (x & (a - 1)) == 0; +} + +/// Return the maximum of the three arguments. +template <typename T> +static T max3(const T &a, const T &b, const T &c) { + return std::max(a, std::max(b, c)); +} + +/// Return the maximum of the three arguments. +template <typename T> +static T min3(const T &a, const T &b, const T &c) { + return std::min(a, std::min(b, c)); +} + +/// Clamp between two values. +template <typename T> +static T clamp(const T &x, const T &a, const T &b) { + return std::min(std::max(x, a), b); +} + +static float saturate(float f) { + return clamp(f, 0.0f, 1.0f); +} + +// Robust floating point comparisons: +// http://realtimecollisiondetection.net/blog/?p=89 +static bool equal(const float f0, const float f1, const float epsilon = NV_EPSILON) { + //return fabs(f0-f1) <= epsilon; + return fabs(f0 - f1) <= epsilon * max3(1.0f, fabsf(f0), fabsf(f1)); +} + +NV_FORCEINLINE static int ftoi_floor(float val) { + return (int)val; +} + +NV_FORCEINLINE static int ftoi_ceil(float val) { + return (int)ceilf(val); +} + +NV_FORCEINLINE static int ftoi_round(float f) { + return int(floorf(f + 0.5f)); +} + +static bool isZero(const float f, const float epsilon = NV_EPSILON) { + return fabs(f) <= epsilon; +} + +static float lerp(float f0, float f1, float t) { + const float s = 1.0f - t; + return f0 * s + f1 * t; +} + +static float square(float f) { + return f * f; +} + +static int square(int i) { + return i * i; +} + +/** Return the next power of two. +* @see http://graphics.stanford.edu/~seander/bithacks.html +* @warning Behaviour for 0 is undefined. +* @note isPowerOfTwo(x) == true -> nextPowerOfTwo(x) == x +* @note nextPowerOfTwo(x) = 2 << log2(x-1) +*/ +static uint32_t nextPowerOfTwo(uint32_t x) { + xaDebugAssert(x != 0); + // On modern CPUs this is supposed to be as fast as using the bsr instruction. + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return x + 1; +} + +static uint64_t nextPowerOfTwo(uint64_t x) { + xaDebugAssert(x != 0); + uint32_t p = 1; + while (x > p) { + p += p; + } + return p; +} + +static uint32_t sdbmHash(const void *data_in, uint32_t size, uint32_t h = 5381) { + const uint8_t *data = (const uint8_t *)data_in; + uint32_t i = 0; + while (i < size) { + h = (h << 16) + (h << 6) - h + (uint32_t)data[i++]; + } + return h; +} + +// Note that this hash does not handle NaN properly. +static uint32_t sdbmFloatHash(const float *f, uint32_t count, uint32_t h = 5381) { + for (uint32_t i = 0; i < count; i++) { + union { + float f; + uint32_t i; + } x = { f[i] }; + if (x.i == 0x80000000) x.i = 0; + h = sdbmHash(&x, 4, h); + } + return h; +} + +template <typename T> +static uint32_t hash(const T &t, uint32_t h = 5381) { + return sdbmHash(&t, sizeof(T), h); +} + +static uint32_t hash(const float &f, uint32_t h) { + return sdbmFloatHash(&f, 1, h); +} + +// Functors for hash table: +template <typename Key> +struct Hash { + uint32_t operator()(const Key &k) const { return hash(k); } +}; + +template <typename Key> +struct Equal { + bool operator()(const Key &k0, const Key &k1) const { return k0 == k1; } +}; + +class Vector2 { +public: + typedef Vector2 const &Arg; + + Vector2() {} + explicit Vector2(float f) : + x(f), + y(f) {} + Vector2(float x, float y) : + x(x), + y(y) {} + Vector2(Vector2::Arg v) : + x(v.x), + y(v.y) {} + + const Vector2 &operator=(Vector2::Arg v) { + x = v.x; + y = v.y; + return *this; + } + const float *ptr() const { return &x; } + + void set(float _x, float _y) { + x = _x; + y = _y; + } + + Vector2 operator-() const { + return Vector2(-x, -y); + } + + void operator+=(Vector2::Arg v) { + x += v.x; + y += v.y; + } + + void operator-=(Vector2::Arg v) { + x -= v.x; + y -= v.y; + } + + void operator*=(float s) { + x *= s; + y *= s; + } + + void operator*=(Vector2::Arg v) { + x *= v.x; + y *= v.y; + } + + friend bool operator==(Vector2::Arg a, Vector2::Arg b) { + return a.x == b.x && a.y == b.y; + } + + friend bool operator!=(Vector2::Arg a, Vector2::Arg b) { + return a.x != b.x || a.y != b.y; + } + + union { +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4201) +#endif + struct + { + float x, y; + }; +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + float component[2]; + }; +}; + +Vector2 operator+(Vector2::Arg a, Vector2::Arg b) { + return Vector2(a.x + b.x, a.y + b.y); +} + +Vector2 operator-(Vector2::Arg a, Vector2::Arg b) { + return Vector2(a.x - b.x, a.y - b.y); +} + +Vector2 operator*(Vector2::Arg v, float s) { + return Vector2(v.x * s, v.y * s); +} + +Vector2 operator*(Vector2::Arg v1, Vector2::Arg v2) { + return Vector2(v1.x * v2.x, v1.y * v2.y); +} + +Vector2 operator/(Vector2::Arg v, float s) { + return Vector2(v.x / s, v.y / s); +} + +Vector2 lerp(Vector2::Arg v1, Vector2::Arg v2, float t) { + const float s = 1.0f - t; + return Vector2(v1.x * s + t * v2.x, v1.y * s + t * v2.y); +} + +float dot(Vector2::Arg a, Vector2::Arg b) { + return a.x * b.x + a.y * b.y; +} + +float lengthSquared(Vector2::Arg v) { + return v.x * v.x + v.y * v.y; +} + +float length(Vector2::Arg v) { + return sqrtf(lengthSquared(v)); +} + +float distance(Vector2::Arg a, Vector2::Arg b) { + return length(a - b); +} + +bool isNormalized(Vector2::Arg v, float epsilon = NV_NORMAL_EPSILON) { + return equal(length(v), 1, epsilon); +} + +Vector2 normalize(Vector2::Arg v, float epsilon = NV_EPSILON) { + float l = length(v); + xaDebugAssert(!isZero(l, epsilon)); +#ifdef NDEBUG + epsilon = 0; // silence unused parameter warning +#endif + Vector2 n = v * (1.0f / l); + xaDebugAssert(isNormalized(n)); + return n; +} + +Vector2 normalizeSafe(Vector2::Arg v, Vector2::Arg fallback, float epsilon = NV_EPSILON) { + float l = length(v); + if (isZero(l, epsilon)) { + return fallback; + } + return v * (1.0f / l); +} + +bool equal(Vector2::Arg v1, Vector2::Arg v2, float epsilon = NV_EPSILON) { + return equal(v1.x, v2.x, epsilon) && equal(v1.y, v2.y, epsilon); +} + +Vector2 max(Vector2::Arg a, Vector2::Arg b) { + return Vector2(std::max(a.x, b.x), std::max(a.y, b.y)); +} + +bool isFinite(Vector2::Arg v) { + return std::isfinite(v.x) && std::isfinite(v.y); +} + +// Note, this is the area scaled by 2! +float triangleArea(Vector2::Arg v0, Vector2::Arg v1) { + return (v0.x * v1.y - v0.y * v1.x); // * 0.5f; +} +float triangleArea(Vector2::Arg a, Vector2::Arg b, Vector2::Arg c) { + // IC: While it may be appealing to use the following expression: + //return (c.x * a.y + a.x * b.y + b.x * c.y - b.x * a.y - c.x * b.y - a.x * c.y); // * 0.5f; + // That's actually a terrible idea. Small triangles far from the origin can end up producing fairly large floating point + // numbers and the results becomes very unstable and dependent on the order of the factors. + // Instead, it's preferable to subtract the vertices first, and multiply the resulting small values together. The result + // in this case is always much more accurate (as long as the triangle is small) and less dependent of the location of + // the triangle. + //return ((a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x)); // * 0.5f; + return triangleArea(a - c, b - c); +} + +float triangleArea2(Vector2::Arg v1, Vector2::Arg v2, Vector2::Arg v3) { + return 0.5f * (v3.x * v1.y + v1.x * v2.y + v2.x * v3.y - v2.x * v1.y - v3.x * v2.y - v1.x * v3.y); +} + +static uint32_t hash(const Vector2 &v, uint32_t h) { + return sdbmFloatHash(v.component, 2, h); +} + +class Vector3 { +public: + typedef Vector3 const &Arg; + + Vector3() {} + explicit Vector3(float f) : + x(f), + y(f), + z(f) {} + Vector3(float x, float y, float z) : + x(x), + y(y), + z(z) {} + Vector3(Vector2::Arg v, float z) : + x(v.x), + y(v.y), + z(z) {} + Vector3(Vector3::Arg v) : + x(v.x), + y(v.y), + z(v.z) {} + + const Vector3 &operator=(Vector3::Arg v) { + x = v.x; + y = v.y; + z = v.z; + return *this; + } + + Vector2 xy() const { + return Vector2(x, y); + } + + const float *ptr() const { return &x; } + + void set(float _x, float _y, float _z) { + x = _x; + y = _y; + z = _z; + } + + Vector3 operator-() const { + return Vector3(-x, -y, -z); + } + + void operator+=(Vector3::Arg v) { + x += v.x; + y += v.y; + z += v.z; + } + + void operator-=(Vector3::Arg v) { + x -= v.x; + y -= v.y; + z -= v.z; + } + + void operator*=(float s) { + x *= s; + y *= s; + z *= s; + } + + void operator/=(float s) { + float is = 1.0f / s; + x *= is; + y *= is; + z *= is; + } + + void operator*=(Vector3::Arg v) { + x *= v.x; + y *= v.y; + z *= v.z; + } + + void operator/=(Vector3::Arg v) { + x /= v.x; + y /= v.y; + z /= v.z; + } + + friend bool operator==(Vector3::Arg a, Vector3::Arg b) { + return a.x == b.x && a.y == b.y && a.z == b.z; + } + + friend bool operator!=(Vector3::Arg a, Vector3::Arg b) { + return a.x != b.x || a.y != b.y || a.z != b.z; + } + + union { +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4201) +#endif + struct + { + float x, y, z; + }; +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + float component[3]; + }; +}; + +Vector3 add(Vector3::Arg a, Vector3::Arg b) { + return Vector3(a.x + b.x, a.y + b.y, a.z + b.z); +} +Vector3 add(Vector3::Arg a, float b) { + return Vector3(a.x + b, a.y + b, a.z + b); +} +Vector3 operator+(Vector3::Arg a, Vector3::Arg b) { + return add(a, b); +} +Vector3 operator+(Vector3::Arg a, float b) { + return add(a, b); +} + +Vector3 sub(Vector3::Arg a, Vector3::Arg b) { + return Vector3(a.x - b.x, a.y - b.y, a.z - b.z); +} + +Vector3 sub(Vector3::Arg a, float b) { + return Vector3(a.x - b, a.y - b, a.z - b); +} + +Vector3 operator-(Vector3::Arg a, Vector3::Arg b) { + return sub(a, b); +} + +Vector3 operator-(Vector3::Arg a, float b) { + return sub(a, b); +} + +Vector3 cross(Vector3::Arg a, Vector3::Arg b) { + return Vector3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); +} + +Vector3 operator*(Vector3::Arg v, float s) { + return Vector3(v.x * s, v.y * s, v.z * s); +} + +Vector3 operator*(float s, Vector3::Arg v) { + return Vector3(v.x * s, v.y * s, v.z * s); +} + +Vector3 operator*(Vector3::Arg v, Vector3::Arg s) { + return Vector3(v.x * s.x, v.y * s.y, v.z * s.z); +} + +Vector3 operator/(Vector3::Arg v, float s) { + return v * (1.0f / s); +} + +Vector3 lerp(Vector3::Arg v1, Vector3::Arg v2, float t) { + const float s = 1.0f - t; + return Vector3(v1.x * s + t * v2.x, v1.y * s + t * v2.y, v1.z * s + t * v2.z); +} + +float dot(Vector3::Arg a, Vector3::Arg b) { + return a.x * b.x + a.y * b.y + a.z * b.z; +} + +float lengthSquared(Vector3::Arg v) { + return v.x * v.x + v.y * v.y + v.z * v.z; +} + +float length(Vector3::Arg v) { + return sqrtf(lengthSquared(v)); +} + +float distance(Vector3::Arg a, Vector3::Arg b) { + return length(a - b); +} + +float distanceSquared(Vector3::Arg a, Vector3::Arg b) { + return lengthSquared(a - b); +} + +bool isNormalized(Vector3::Arg v, float epsilon = NV_NORMAL_EPSILON) { + return equal(length(v), 1, epsilon); +} + +Vector3 normalize(Vector3::Arg v, float epsilon = NV_EPSILON) { + float l = length(v); + xaDebugAssert(!isZero(l, epsilon)); +#ifdef NDEBUG + epsilon = 0; // silence unused parameter warning +#endif + Vector3 n = v * (1.0f / l); + xaDebugAssert(isNormalized(n)); + return n; +} + +Vector3 normalizeSafe(Vector3::Arg v, Vector3::Arg fallback, float epsilon = NV_EPSILON) { + float l = length(v); + if (isZero(l, epsilon)) { + return fallback; + } + return v * (1.0f / l); +} + +bool equal(Vector3::Arg v1, Vector3::Arg v2, float epsilon = NV_EPSILON) { + return equal(v1.x, v2.x, epsilon) && equal(v1.y, v2.y, epsilon) && equal(v1.z, v2.z, epsilon); +} + +Vector3 min(Vector3::Arg a, Vector3::Arg b) { + return Vector3(std::min(a.x, b.x), std::min(a.y, b.y), std::min(a.z, b.z)); +} + +Vector3 max(Vector3::Arg a, Vector3::Arg b) { + return Vector3(std::max(a.x, b.x), std::max(a.y, b.y), std::max(a.z, b.z)); +} + +Vector3 clamp(Vector3::Arg v, float min, float max) { + return Vector3(clamp(v.x, min, max), clamp(v.y, min, max), clamp(v.z, min, max)); +} + +Vector3 saturate(Vector3::Arg v) { + return Vector3(saturate(v.x), saturate(v.y), saturate(v.z)); +} + +Vector3 floor(Vector3::Arg v) { + return Vector3(floorf(v.x), floorf(v.y), floorf(v.z)); +} + +bool isFinite(Vector3::Arg v) { + return std::isfinite(v.x) && std::isfinite(v.y) && std::isfinite(v.z); +} + +static uint32_t hash(const Vector3 &v, uint32_t h) { + return sdbmFloatHash(v.component, 3, h); +} + +/// Basis class to compute tangent space basis, ortogonalizations and to +/// transform vectors from one space to another. +class Basis { +public: + /// Create a null basis. + Basis() : + tangent(0, 0, 0), + bitangent(0, 0, 0), + normal(0, 0, 0) {} + + void buildFrameForDirection(Vector3::Arg d, float angle = 0) { + xaAssert(isNormalized(d)); + normal = d; + // Choose minimum axis. + if (fabsf(normal.x) < fabsf(normal.y) && fabsf(normal.x) < fabsf(normal.z)) { + tangent = Vector3(1, 0, 0); + } else if (fabsf(normal.y) < fabsf(normal.z)) { + tangent = Vector3(0, 1, 0); + } else { + tangent = Vector3(0, 0, 1); + } + // Ortogonalize + tangent -= normal * dot(normal, tangent); + tangent = normalize(tangent); + bitangent = cross(normal, tangent); + // Rotate frame around normal according to angle. + if (angle != 0.0f) { + float c = cosf(angle); + float s = sinf(angle); + Vector3 tmp = c * tangent - s * bitangent; + bitangent = s * tangent + c * bitangent; + tangent = tmp; + } + } + + Vector3 tangent; + Vector3 bitangent; + Vector3 normal; +}; + +// Simple bit array. +class BitArray { +public: + BitArray() : + m_size(0) {} + BitArray(uint32_t sz) { + resize(sz); + } + + uint32_t size() const { + return m_size; + } + + void clear() { + resize(0); + } + + void resize(uint32_t new_size) { + m_size = new_size; + m_wordArray.resize((m_size + 31) >> 5); + } + + /// Get bit. + bool bitAt(uint32_t b) const { + xaDebugAssert(b < m_size); + return (m_wordArray[b >> 5] & (1 << (b & 31))) != 0; + } + + // Set a bit. + void setBitAt(uint32_t idx) { + xaDebugAssert(idx < m_size); + m_wordArray[idx >> 5] |= (1 << (idx & 31)); + } + + // Toggle a bit. + void toggleBitAt(uint32_t idx) { + xaDebugAssert(idx < m_size); + m_wordArray[idx >> 5] ^= (1 << (idx & 31)); + } + + // Set a bit to the given value. @@ Rename modifyBitAt? + void setBitAt(uint32_t idx, bool b) { + xaDebugAssert(idx < m_size); + m_wordArray[idx >> 5] = setBits(m_wordArray[idx >> 5], 1 << (idx & 31), b); + xaDebugAssert(bitAt(idx) == b); + } + + // Clear all the bits. + void clearAll() { + memset(m_wordArray.data(), 0, m_wordArray.size() * sizeof(uint32_t)); + } + + // Set all the bits. + void setAll() { + memset(m_wordArray.data(), 0xFF, m_wordArray.size() * sizeof(uint32_t)); + } + +private: + // See "Conditionally set or clear bits without branching" at http://graphics.stanford.edu/~seander/bithacks.html + uint32_t setBits(uint32_t w, uint32_t m, bool b) { + return (w & ~m) | (-int(b) & m); + } + + // Number of bits stored. + uint32_t m_size; + + // Array of bits. + std::vector<uint32_t> m_wordArray; +}; + +/// Bit map. This should probably be called BitImage. +class BitMap { +public: + BitMap() : + m_width(0), + m_height(0) {} + BitMap(uint32_t w, uint32_t h) : + m_width(w), + m_height(h), + m_bitArray(w * h) {} + + uint32_t width() const { + return m_width; + } + uint32_t height() const { + return m_height; + } + + void resize(uint32_t w, uint32_t h, bool initValue) { + BitArray tmp(w * h); + if (initValue) + tmp.setAll(); + else + tmp.clearAll(); + // @@ Copying one bit at a time. This could be much faster. + for (uint32_t y = 0; y < m_height; y++) { + for (uint32_t x = 0; x < m_width; x++) { + //tmp.setBitAt(y*w + x, bitAt(x, y)); + if (bitAt(x, y) != initValue) tmp.toggleBitAt(y * w + x); + } + } + std::swap(m_bitArray, tmp); + m_width = w; + m_height = h; + } + + bool bitAt(uint32_t x, uint32_t y) const { + xaDebugAssert(x < m_width && y < m_height); + return m_bitArray.bitAt(y * m_width + x); + } + + void setBitAt(uint32_t x, uint32_t y) { + xaDebugAssert(x < m_width && y < m_height); + m_bitArray.setBitAt(y * m_width + x); + } + + void clearAll() { + m_bitArray.clearAll(); + } + +private: + uint32_t m_width; + uint32_t m_height; + BitArray m_bitArray; +}; + +// Axis Aligned Bounding Box. +class Box { +public: + Box() {} + Box(const Box &b) : + minCorner(b.minCorner), + maxCorner(b.maxCorner) {} + Box(const Vector3 &mins, const Vector3 &maxs) : + minCorner(mins), + maxCorner(maxs) {} + + operator const float *() const { + return reinterpret_cast<const float *>(this); + } + + // Clear the bounds. + void clearBounds() { + minCorner.set(FLT_MAX, FLT_MAX, FLT_MAX); + maxCorner.set(-FLT_MAX, -FLT_MAX, -FLT_MAX); + } + + // Return extents of the box. + Vector3 extents() const { + return (maxCorner - minCorner) * 0.5f; + } + + // Add a point to this box. + void addPointToBounds(const Vector3 &p) { + minCorner = min(minCorner, p); + maxCorner = max(maxCorner, p); + } + + // Get the volume of the box. + float volume() const { + Vector3 d = extents(); + return 8.0f * (d.x * d.y * d.z); + } + + Vector3 minCorner; + Vector3 maxCorner; +}; + +class Fit { +public: + static Vector3 computeCentroid(int n, const Vector3 *__restrict points) { + Vector3 centroid(0.0f); + for (int i = 0; i < n; i++) { + centroid += points[i]; + } + centroid /= float(n); + return centroid; + } + + static Vector3 computeCovariance(int n, const Vector3 *__restrict points, float *__restrict covariance) { + // compute the centroid + Vector3 centroid = computeCentroid(n, points); + // compute covariance matrix + for (int i = 0; i < 6; i++) { + covariance[i] = 0.0f; + } + for (int i = 0; i < n; i++) { + Vector3 v = points[i] - centroid; + covariance[0] += v.x * v.x; + covariance[1] += v.x * v.y; + covariance[2] += v.x * v.z; + covariance[3] += v.y * v.y; + covariance[4] += v.y * v.z; + covariance[5] += v.z * v.z; + } + return centroid; + } + + static bool isPlanar(int n, const Vector3 *points, float epsilon = NV_EPSILON) { + // compute the centroid and covariance + float matrix[6]; + computeCovariance(n, points, matrix); + float eigenValues[3]; + Vector3 eigenVectors[3]; + if (!eigenSolveSymmetric3(matrix, eigenValues, eigenVectors)) { + return false; + } + return eigenValues[2] < epsilon; + } + + // Tridiagonal solver from Charles Bloom. + // Householder transforms followed by QL decomposition. + // Seems to be based on the code from Numerical Recipes in C. + static bool eigenSolveSymmetric3(const float matrix[6], float eigenValues[3], Vector3 eigenVectors[3]) { + xaDebugAssert(matrix != NULL && eigenValues != NULL && eigenVectors != NULL); + float subd[3]; + float diag[3]; + float work[3][3]; + work[0][0] = matrix[0]; + work[0][1] = work[1][0] = matrix[1]; + work[0][2] = work[2][0] = matrix[2]; + work[1][1] = matrix[3]; + work[1][2] = work[2][1] = matrix[4]; + work[2][2] = matrix[5]; + EigenSolver3_Tridiagonal(work, diag, subd); + if (!EigenSolver3_QLAlgorithm(work, diag, subd)) { + for (int i = 0; i < 3; i++) { + eigenValues[i] = 0; + eigenVectors[i] = Vector3(0); + } + return false; + } + for (int i = 0; i < 3; i++) { + eigenValues[i] = (float)diag[i]; + } + // eigenvectors are the columns; make them the rows : + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + eigenVectors[j].component[i] = (float)work[i][j]; + } + } + // shuffle to sort by singular value : + if (eigenValues[2] > eigenValues[0] && eigenValues[2] > eigenValues[1]) { + std::swap(eigenValues[0], eigenValues[2]); + std::swap(eigenVectors[0], eigenVectors[2]); + } + if (eigenValues[1] > eigenValues[0]) { + std::swap(eigenValues[0], eigenValues[1]); + std::swap(eigenVectors[0], eigenVectors[1]); + } + if (eigenValues[2] > eigenValues[1]) { + std::swap(eigenValues[1], eigenValues[2]); + std::swap(eigenVectors[1], eigenVectors[2]); + } + xaDebugAssert(eigenValues[0] >= eigenValues[1] && eigenValues[0] >= eigenValues[2]); + xaDebugAssert(eigenValues[1] >= eigenValues[2]); + return true; + } + +private: + static void EigenSolver3_Tridiagonal(float mat[3][3], float *diag, float *subd) { + // Householder reduction T = Q^t M Q + // Input: + // mat, symmetric 3x3 matrix M + // Output: + // mat, orthogonal matrix Q + // diag, diagonal entries of T + // subd, subdiagonal entries of T (T is symmetric) + const float epsilon = 1e-08f; + float a = mat[0][0]; + float b = mat[0][1]; + float c = mat[0][2]; + float d = mat[1][1]; + float e = mat[1][2]; + float f = mat[2][2]; + diag[0] = a; + subd[2] = 0.f; + if (fabsf(c) >= epsilon) { + const float ell = sqrtf(b * b + c * c); + b /= ell; + c /= ell; + const float q = 2 * b * e + c * (f - d); + diag[1] = d + c * q; + diag[2] = f - c * q; + subd[0] = ell; + subd[1] = e - b * q; + mat[0][0] = 1; + mat[0][1] = 0; + mat[0][2] = 0; + mat[1][0] = 0; + mat[1][1] = b; + mat[1][2] = c; + mat[2][0] = 0; + mat[2][1] = c; + mat[2][2] = -b; + } else { + diag[1] = d; + diag[2] = f; + subd[0] = b; + subd[1] = e; + mat[0][0] = 1; + mat[0][1] = 0; + mat[0][2] = 0; + mat[1][0] = 0; + mat[1][1] = 1; + mat[1][2] = 0; + mat[2][0] = 0; + mat[2][1] = 0; + mat[2][2] = 1; + } + } + + static bool EigenSolver3_QLAlgorithm(float mat[3][3], float *diag, float *subd) { + // QL iteration with implicit shifting to reduce matrix from tridiagonal + // to diagonal + const int maxiter = 32; + for (int ell = 0; ell < 3; ell++) { + int iter; + for (iter = 0; iter < maxiter; iter++) { + int m; + for (m = ell; m <= 1; m++) { + float dd = fabsf(diag[m]) + fabsf(diag[m + 1]); + if (fabsf(subd[m]) + dd == dd) + break; + } + if (m == ell) + break; + float g = (diag[ell + 1] - diag[ell]) / (2 * subd[ell]); + float r = sqrtf(g * g + 1); + if (g < 0) + g = diag[m] - diag[ell] + subd[ell] / (g - r); + else + g = diag[m] - diag[ell] + subd[ell] / (g + r); + float s = 1, c = 1, p = 0; + for (int i = m - 1; i >= ell; i--) { + float f = s * subd[i], b = c * subd[i]; + if (fabsf(f) >= fabsf(g)) { + c = g / f; + r = sqrtf(c * c + 1); + subd[i + 1] = f * r; + c *= (s = 1 / r); + } else { + s = f / g; + r = sqrtf(s * s + 1); + subd[i + 1] = g * r; + s *= (c = 1 / r); + } + g = diag[i + 1] - p; + r = (diag[i] - g) * s + 2 * b * c; + p = s * r; + diag[i + 1] = g + p; + g = c * r - b; + for (int k = 0; k < 3; k++) { + f = mat[k][i + 1]; + mat[k][i + 1] = s * mat[k][i] + c * f; + mat[k][i] = c * mat[k][i] - s * f; + } + } + diag[ell] -= p; + subd[ell] = g; + subd[m] = 0; + } + if (iter == maxiter) + // should not get here under normal circumstances + return false; + } + return true; + } +}; + +/// Fixed size vector class. +class FullVector { +public: + FullVector(uint32_t dim) { m_array.resize(dim); } + FullVector(const FullVector &v) : + m_array(v.m_array) {} + + const FullVector &operator=(const FullVector &v) { + xaAssert(dimension() == v.dimension()); + m_array = v.m_array; + return *this; + } + + uint32_t dimension() const { return m_array.size(); } + const float &operator[](uint32_t index) const { return m_array[index]; } + float &operator[](uint32_t index) { return m_array[index]; } + + void fill(float f) { + const uint32_t dim = dimension(); + for (uint32_t i = 0; i < dim; i++) { + m_array[i] = f; + } + } + + void operator+=(const FullVector &v) { + xaDebugAssert(dimension() == v.dimension()); + const uint32_t dim = dimension(); + for (uint32_t i = 0; i < dim; i++) { + m_array[i] += v.m_array[i]; + } + } + + void operator-=(const FullVector &v) { + xaDebugAssert(dimension() == v.dimension()); + const uint32_t dim = dimension(); + for (uint32_t i = 0; i < dim; i++) { + m_array[i] -= v.m_array[i]; + } + } + + void operator*=(const FullVector &v) { + xaDebugAssert(dimension() == v.dimension()); + const uint32_t dim = dimension(); + for (uint32_t i = 0; i < dim; i++) { + m_array[i] *= v.m_array[i]; + } + } + + void operator+=(float f) { + const uint32_t dim = dimension(); + for (uint32_t i = 0; i < dim; i++) { + m_array[i] += f; + } + } + + void operator-=(float f) { + const uint32_t dim = dimension(); + for (uint32_t i = 0; i < dim; i++) { + m_array[i] -= f; + } + } + + void operator*=(float f) { + const uint32_t dim = dimension(); + for (uint32_t i = 0; i < dim; i++) { + m_array[i] *= f; + } + } + +private: + std::vector<float> m_array; +}; + +namespace halfedge { +class Face; +class Vertex; + +class Edge { +public: + uint32_t id; + Edge *next; + Edge *prev; // This is not strictly half-edge, but makes algorithms easier and faster. + Edge *pair; + Vertex *vertex; + Face *face; + + // Default constructor. + Edge(uint32_t id) : + id(id), + next(NULL), + prev(NULL), + pair(NULL), + vertex(NULL), + face(NULL) {} + + // Vertex queries. + const Vertex *from() const { + return vertex; + } + + Vertex *from() { + return vertex; + } + + const Vertex *to() const { + return pair->vertex; // This used to be 'next->vertex', but that changed often when the connectivity of the mesh changes. + } + + Vertex *to() { + return pair->vertex; + } + + // Edge queries. + void setNext(Edge *e) { + next = e; + if (e != NULL) e->prev = this; + } + void setPrev(Edge *e) { + prev = e; + if (e != NULL) e->next = this; + } + + // @@ It would be more simple to only check m_pair == NULL + // Face queries. + bool isBoundary() const { + return !(face && pair->face); + } + + // @@ This is not exactly accurate, we should compare the texture coordinates... + bool isSeam() const { + return vertex != pair->next->vertex || next->vertex != pair->vertex; + } + + bool isNormalSeam() const; + bool isTextureSeam() const; + + bool isValid() const { + // null face is OK. + if (next == NULL || prev == NULL || pair == NULL || vertex == NULL) return false; + if (next->prev != this) return false; + if (prev->next != this) return false; + if (pair->pair != this) return false; + return true; + } + + float length() const; + + // Return angle between this edge and the previous one. + float angle() const; +}; + +class Vertex { +public: + uint32_t id; + uint32_t original_id; + Edge *edge; + Vertex *next; + Vertex *prev; + Vector3 pos; + Vector3 nor; + Vector2 tex; + + Vertex(uint32_t id) : + id(id), + original_id(id), + edge(NULL), + pos(0.0f), + nor(0.0f), + tex(0.0f) { + next = this; + prev = this; + } + + // Set first edge of all colocals. + void setEdge(Edge *e) { + for (VertexIterator it(colocals()); !it.isDone(); it.advance()) { + it.current()->edge = e; + } + } + + // Update position of all colocals. + void setPos(const Vector3 &p) { + for (VertexIterator it(colocals()); !it.isDone(); it.advance()) { + it.current()->pos = p; + } + } + + bool isFirstColocal() const { + return firstColocal() == this; + } + + const Vertex *firstColocal() const { + uint32_t firstId = id; + const Vertex *vertex = this; + for (ConstVertexIterator it(colocals()); !it.isDone(); it.advance()) { + if (it.current()->id < firstId) { + firstId = vertex->id; + vertex = it.current(); + } + } + return vertex; + } + + Vertex *firstColocal() { + Vertex *vertex = this; + uint32_t firstId = id; + for (VertexIterator it(colocals()); !it.isDone(); it.advance()) { + if (it.current()->id < firstId) { + firstId = vertex->id; + vertex = it.current(); + } + } + return vertex; + } + + bool isColocal(const Vertex *v) const { + if (this == v) return true; + if (pos != v->pos) return false; + for (ConstVertexIterator it(colocals()); !it.isDone(); it.advance()) { + if (v == it.current()) { + return true; + } + } + return false; + } + + void linkColocal(Vertex *v) { + next->prev = v; + v->next = next; + next = v; + v->prev = this; + } + void unlinkColocal() { + next->prev = prev; + prev->next = next; + next = this; + prev = this; + } + + // @@ Note: This only works if linkBoundary has been called. + bool isBoundary() const { + return (edge && !edge->face); + } + + // Iterator that visits the edges around this vertex in counterclockwise order. + class EdgeIterator //: public Iterator<Edge *> + { + public: + EdgeIterator(Edge *e) : + m_end(NULL), + m_current(e) {} + + virtual void advance() { + if (m_end == NULL) m_end = m_current; + m_current = m_current->pair->next; + //m_current = m_current->prev->pair; + } + + virtual bool isDone() const { + return m_end == m_current; + } + virtual Edge *current() const { + return m_current; + } + Vertex *vertex() const { + return m_current->vertex; + } + + private: + Edge *m_end; + Edge *m_current; + }; + + EdgeIterator edges() { + return EdgeIterator(edge); + } + EdgeIterator edges(Edge *e) { + return EdgeIterator(e); + } + + // Iterator that visits the edges around this vertex in counterclockwise order. + class ConstEdgeIterator //: public Iterator<Edge *> + { + public: + ConstEdgeIterator(const Edge *e) : + m_end(NULL), + m_current(e) {} + ConstEdgeIterator(EdgeIterator it) : + m_end(NULL), + m_current(it.current()) {} + + virtual void advance() { + if (m_end == NULL) m_end = m_current; + m_current = m_current->pair->next; + //m_current = m_current->prev->pair; + } + + virtual bool isDone() const { + return m_end == m_current; + } + virtual const Edge *current() const { + return m_current; + } + const Vertex *vertex() const { + return m_current->to(); + } + + private: + const Edge *m_end; + const Edge *m_current; + }; + + ConstEdgeIterator edges() const { + return ConstEdgeIterator(edge); + } + ConstEdgeIterator edges(const Edge *e) const { + return ConstEdgeIterator(e); + } + + // Iterator that visits all the colocal vertices. + class VertexIterator //: public Iterator<Edge *> + { + public: + VertexIterator(Vertex *v) : + m_end(NULL), + m_current(v) {} + + virtual void advance() { + if (m_end == NULL) m_end = m_current; + m_current = m_current->next; + } + + virtual bool isDone() const { + return m_end == m_current; + } + virtual Vertex *current() const { + return m_current; + } + + private: + Vertex *m_end; + Vertex *m_current; + }; + + VertexIterator colocals() { + return VertexIterator(this); + } + + // Iterator that visits all the colocal vertices. + class ConstVertexIterator //: public Iterator<Edge *> + { + public: + ConstVertexIterator(const Vertex *v) : + m_end(NULL), + m_current(v) {} + + virtual void advance() { + if (m_end == NULL) m_end = m_current; + m_current = m_current->next; + } + + virtual bool isDone() const { + return m_end == m_current; + } + virtual const Vertex *current() const { + return m_current; + } + + private: + const Vertex *m_end; + const Vertex *m_current; + }; + + ConstVertexIterator colocals() const { + return ConstVertexIterator(this); + } +}; + +bool Edge::isNormalSeam() const { + return (vertex->nor != pair->next->vertex->nor || next->vertex->nor != pair->vertex->nor); +} + +bool Edge::isTextureSeam() const { + return (vertex->tex != pair->next->vertex->tex || next->vertex->tex != pair->vertex->tex); +} + +float Edge::length() const { + return internal::length(to()->pos - from()->pos); +} + +float Edge::angle() const { + Vector3 p = vertex->pos; + Vector3 a = prev->vertex->pos; + Vector3 b = next->vertex->pos; + Vector3 v0 = a - p; + Vector3 v1 = b - p; + return acosf(dot(v0, v1) / (internal::length(v0) * internal::length(v1))); +} + +class Face { +public: + uint32_t id; + uint16_t group; + uint16_t material; + Edge *edge; + + Face(uint32_t id) : + id(id), + group(uint16_t(~0)), + material(uint16_t(~0)), + edge(NULL) {} + + float area() const { + float area = 0; + const Vector3 &v0 = edge->from()->pos; + for (ConstEdgeIterator it(edges(edge->next)); it.current() != edge->prev; it.advance()) { + const Edge *e = it.current(); + const Vector3 &v1 = e->vertex->pos; + const Vector3 &v2 = e->next->vertex->pos; + area += length(cross(v1 - v0, v2 - v0)); + } + return area * 0.5f; + } + + float parametricArea() const { + float area = 0; + const Vector2 &v0 = edge->from()->tex; + for (ConstEdgeIterator it(edges(edge->next)); it.current() != edge->prev; it.advance()) { + const Edge *e = it.current(); + const Vector2 &v1 = e->vertex->tex; + const Vector2 &v2 = e->next->vertex->tex; + area += triangleArea(v0, v1, v2); + } + return area * 0.5f; + } + + Vector3 normal() const { + Vector3 n(0); + const Vertex *vertex0 = NULL; + for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) { + const Edge *e = it.current(); + xaAssert(e != NULL); + if (vertex0 == NULL) { + vertex0 = e->vertex; + } else if (e->next->vertex != vertex0) { + const halfedge::Vertex *vertex1 = e->from(); + const halfedge::Vertex *vertex2 = e->to(); + const Vector3 &p0 = vertex0->pos; + const Vector3 &p1 = vertex1->pos; + const Vector3 &p2 = vertex2->pos; + Vector3 v10 = p1 - p0; + Vector3 v20 = p2 - p0; + n += cross(v10, v20); + } + } + return normalizeSafe(n, Vector3(0, 0, 1), 0.0f); + } + + Vector3 centroid() const { + Vector3 sum(0.0f); + uint32_t count = 0; + for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) { + const Edge *e = it.current(); + sum += e->from()->pos; + count++; + } + return sum / float(count); + } + + // Unnormalized face normal assuming it's a triangle. + Vector3 triangleNormal() const { + Vector3 p0 = edge->vertex->pos; + Vector3 p1 = edge->next->vertex->pos; + Vector3 p2 = edge->next->next->vertex->pos; + Vector3 e0 = p2 - p0; + Vector3 e1 = p1 - p0; + return normalizeSafe(cross(e0, e1), Vector3(0), 0.0f); + } + + Vector3 triangleNormalAreaScaled() const { + Vector3 p0 = edge->vertex->pos; + Vector3 p1 = edge->next->vertex->pos; + Vector3 p2 = edge->next->next->vertex->pos; + Vector3 e0 = p2 - p0; + Vector3 e1 = p1 - p0; + return cross(e0, e1); + } + + // Average of the edge midpoints weighted by the edge length. + // I want a point inside the triangle, but closer to the cirumcenter. + Vector3 triangleCenter() const { + Vector3 p0 = edge->vertex->pos; + Vector3 p1 = edge->next->vertex->pos; + Vector3 p2 = edge->next->next->vertex->pos; + float l0 = length(p1 - p0); + float l1 = length(p2 - p1); + float l2 = length(p0 - p2); + Vector3 m0 = (p0 + p1) * l0 / (l0 + l1 + l2); + Vector3 m1 = (p1 + p2) * l1 / (l0 + l1 + l2); + Vector3 m2 = (p2 + p0) * l2 / (l0 + l1 + l2); + return m0 + m1 + m2; + } + + bool isValid() const { + uint32_t count = 0; + for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) { + const Edge *e = it.current(); + if (e->face != this) return false; + if (!e->isValid()) return false; + if (!e->pair->isValid()) return false; + count++; + } + if (count < 3) return false; + return true; + } + + bool contains(const Edge *e) const { + for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) { + if (it.current() == e) return true; + } + return false; + } + + uint32_t edgeCount() const { + uint32_t count = 0; + for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) { + ++count; + } + return count; + } + + // The iterator that visits the edges of this face in clockwise order. + class EdgeIterator //: public Iterator<Edge *> + { + public: + EdgeIterator(Edge *e) : + m_end(NULL), + m_current(e) {} + + virtual void advance() { + if (m_end == NULL) m_end = m_current; + m_current = m_current->next; + } + + virtual bool isDone() const { + return m_end == m_current; + } + virtual Edge *current() const { + return m_current; + } + Vertex *vertex() const { + return m_current->vertex; + } + + private: + Edge *m_end; + Edge *m_current; + }; + + EdgeIterator edges() { + return EdgeIterator(edge); + } + EdgeIterator edges(Edge *e) { + xaDebugAssert(contains(e)); + return EdgeIterator(e); + } + + // The iterator that visits the edges of this face in clockwise order. + class ConstEdgeIterator //: public Iterator<const Edge *> + { + public: + ConstEdgeIterator(const Edge *e) : + m_end(NULL), + m_current(e) {} + ConstEdgeIterator(const EdgeIterator &it) : + m_end(NULL), + m_current(it.current()) {} + + virtual void advance() { + if (m_end == NULL) m_end = m_current; + m_current = m_current->next; + } + + virtual bool isDone() const { + return m_end == m_current; + } + virtual const Edge *current() const { + return m_current; + } + const Vertex *vertex() const { + return m_current->vertex; + } + + private: + const Edge *m_end; + const Edge *m_current; + }; + + ConstEdgeIterator edges() const { + return ConstEdgeIterator(edge); + } + ConstEdgeIterator edges(const Edge *e) const { + xaDebugAssert(contains(e)); + return ConstEdgeIterator(e); + } +}; + +/// Simple half edge mesh designed for dynamic mesh manipulation. +class Mesh { +public: + Mesh() : + m_colocalVertexCount(0) {} + + Mesh(const Mesh *mesh) { + // Copy mesh vertices. + const uint32_t vertexCount = mesh->vertexCount(); + m_vertexArray.resize(vertexCount); + for (uint32_t v = 0; v < vertexCount; v++) { + const Vertex *vertex = mesh->vertexAt(v); + xaDebugAssert(vertex->id == v); + m_vertexArray[v] = new Vertex(v); + m_vertexArray[v]->pos = vertex->pos; + m_vertexArray[v]->nor = vertex->nor; + m_vertexArray[v]->tex = vertex->tex; + } + m_colocalVertexCount = vertexCount; + // Copy mesh faces. + const uint32_t faceCount = mesh->faceCount(); + std::vector<uint32_t> indexArray; + indexArray.reserve(3); + for (uint32_t f = 0; f < faceCount; f++) { + const Face *face = mesh->faceAt(f); + for (Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const Vertex *vertex = it.current()->from(); + indexArray.push_back(vertex->id); + } + addFace(indexArray); + indexArray.clear(); + } + } + + ~Mesh() { + clear(); + } + + void clear() { + for (size_t i = 0; i < m_vertexArray.size(); i++) + delete m_vertexArray[i]; + m_vertexArray.clear(); + for (auto it = m_edgeMap.begin(); it != m_edgeMap.end(); it++) + delete it->second; + m_edgeArray.clear(); + m_edgeMap.clear(); + for (size_t i = 0; i < m_faceArray.size(); i++) + delete m_faceArray[i]; + m_faceArray.clear(); + } + + Vertex *addVertex(const Vector3 &pos) { + xaDebugAssert(isFinite(pos)); + Vertex *v = new Vertex(m_vertexArray.size()); + v->pos = pos; + m_vertexArray.push_back(v); + return v; + } + + /// Link colocal vertices based on geometric location only. + void linkColocals() { + xaPrint("--- Linking colocals:\n"); + const uint32_t vertexCount = this->vertexCount(); + std::unordered_map<Vector3, Vertex *, Hash<Vector3>, Equal<Vector3> > vertexMap; + vertexMap.reserve(vertexCount); + for (uint32_t v = 0; v < vertexCount; v++) { + Vertex *vertex = vertexAt(v); + Vertex *colocal = vertexMap[vertex->pos]; + if (colocal) { + colocal->linkColocal(vertex); + } else { + vertexMap[vertex->pos] = vertex; + } + } + m_colocalVertexCount = vertexMap.size(); + xaPrint("--- %d vertex positions.\n", m_colocalVertexCount); + // @@ Remove duplicated vertices? or just leave them as colocals? + } + + void linkColocalsWithCanonicalMap(const std::vector<uint32_t> &canonicalMap) { + xaPrint("--- Linking colocals:\n"); + uint32_t vertexMapSize = 0; + for (uint32_t i = 0; i < canonicalMap.size(); i++) { + vertexMapSize = std::max(vertexMapSize, canonicalMap[i] + 1); + } + std::vector<Vertex *> vertexMap; + vertexMap.resize(vertexMapSize, NULL); + m_colocalVertexCount = 0; + const uint32_t vertexCount = this->vertexCount(); + for (uint32_t v = 0; v < vertexCount; v++) { + Vertex *vertex = vertexAt(v); + Vertex *colocal = vertexMap[canonicalMap[v]]; + if (colocal != NULL) { + xaDebugAssert(vertex->pos == colocal->pos); + colocal->linkColocal(vertex); + } else { + vertexMap[canonicalMap[v]] = vertex; + m_colocalVertexCount++; + } + } + xaPrint("--- %d vertex positions.\n", m_colocalVertexCount); + } + + Face *addFace() { + Face *f = new Face(m_faceArray.size()); + m_faceArray.push_back(f); + return f; + } + + Face *addFace(uint32_t v0, uint32_t v1, uint32_t v2) { + uint32_t indexArray[3]; + indexArray[0] = v0; + indexArray[1] = v1; + indexArray[2] = v2; + return addFace(indexArray, 3, 0, 3); + } + + Face *addUniqueFace(uint32_t v0, uint32_t v1, uint32_t v2) { + + int base_vertex = m_vertexArray.size(); + + uint32_t ids[3] = { v0, v1, v2 }; + + Vector3 base[3] = { + m_vertexArray[v0]->pos, + m_vertexArray[v1]->pos, + m_vertexArray[v2]->pos, + }; + + //make sure its not a degenerate + bool degenerate = distanceSquared(base[0], base[1]) < NV_EPSILON || distanceSquared(base[0], base[2]) < NV_EPSILON || distanceSquared(base[1], base[2]) < NV_EPSILON; + xaDebugAssert(!degenerate); + + float min_x = 0; + + for (int i = 0; i < 3; i++) { + if (i == 0 || m_vertexArray[v0]->pos.x < min_x) { + min_x = m_vertexArray[v0]->pos.x; + } + } + + float max_x = 0; + + for (int j = 0; j < m_vertexArray.size(); j++) { + if (j == 0 || m_vertexArray[j]->pos.x > max_x) { //vertex already exists + max_x = m_vertexArray[j]->pos.x; + } + } + + //separate from everything else, in x axis + for (int i = 0; i < 3; i++) { + + base[i].x -= min_x; + base[i].x += max_x + 10.0; + } + + for (int i = 0; i < 3; i++) { + Vertex *v = new Vertex(m_vertexArray.size()); + v->pos = base[i]; + v->nor = m_vertexArray[ids[i]]->nor, + v->tex = m_vertexArray[ids[i]]->tex, + + v->original_id = ids[i]; + m_vertexArray.push_back(v); + } + + uint32_t indexArray[3]; + indexArray[0] = base_vertex + 0; + indexArray[1] = base_vertex + 1; + indexArray[2] = base_vertex + 2; + return addFace(indexArray, 3, 0, 3); + } + + Face *addFace(uint32_t v0, uint32_t v1, uint32_t v2, uint32_t v3) { + uint32_t indexArray[4]; + indexArray[0] = v0; + indexArray[1] = v1; + indexArray[2] = v2; + indexArray[3] = v3; + return addFace(indexArray, 4, 0, 4); + } + + Face *addFace(const std::vector<uint32_t> &indexArray) { + return addFace(indexArray, 0, indexArray.size()); + } + + Face *addFace(const std::vector<uint32_t> &indexArray, uint32_t first, uint32_t num) { + return addFace(indexArray.data(), (uint32_t)indexArray.size(), first, num); + } + + Face *addFace(const uint32_t *indexArray, uint32_t indexCount, uint32_t first, uint32_t num) { + xaDebugAssert(first < indexCount); + xaDebugAssert(num <= indexCount - first); + xaDebugAssert(num > 2); + if (!canAddFace(indexArray, first, num)) { + return NULL; + } + Face *f = new Face(m_faceArray.size()); + Edge *firstEdge = NULL; + Edge *last = NULL; + Edge *current = NULL; + for (uint32_t i = 0; i < num - 1; i++) { + current = addEdge(indexArray[first + i], indexArray[first + i + 1]); + xaAssert(current != NULL && current->face == NULL); + current->face = f; + if (last != NULL) + last->setNext(current); + else + firstEdge = current; + last = current; + } + current = addEdge(indexArray[first + num - 1], indexArray[first]); + xaAssert(current != NULL && current->face == NULL); + current->face = f; + last->setNext(current); + current->setNext(firstEdge); + f->edge = firstEdge; + m_faceArray.push_back(f); + return f; + } + + // These functions disconnect the given element from the mesh and delete it. + + // @@ We must always disconnect edge pairs simultaneously. + void disconnect(Edge *edge) { + xaDebugAssert(edge != NULL); + // Remove from edge list. + if ((edge->id & 1) == 0) { + xaDebugAssert(m_edgeArray[edge->id / 2] == edge); + m_edgeArray[edge->id / 2] = NULL; + } + // Remove edge from map. @@ Store map key inside edge? + xaDebugAssert(edge->from() != NULL && edge->to() != NULL); + size_t removed = m_edgeMap.erase(Key(edge->from()->id, edge->to()->id)); + xaDebugAssert(removed == 1); +#ifdef NDEBUG + removed = 0; // silence unused parameter warning +#endif + // Disconnect from vertex. + if (edge->vertex != NULL) { + if (edge->vertex->edge == edge) { + if (edge->prev && edge->prev->pair) { + edge->vertex->edge = edge->prev->pair; + } else if (edge->pair && edge->pair->next) { + edge->vertex->edge = edge->pair->next; + } else { + edge->vertex->edge = NULL; + // @@ Remove disconnected vertex? + } + } + } + // Disconnect from face. + if (edge->face != NULL) { + if (edge->face->edge == edge) { + if (edge->next != NULL && edge->next != edge) { + edge->face->edge = edge->next; + } else if (edge->prev != NULL && edge->prev != edge) { + edge->face->edge = edge->prev; + } else { + edge->face->edge = NULL; + // @@ Remove disconnected face? + } + } + } + // Disconnect from previous. + if (edge->prev) { + if (edge->prev->next == edge) { + edge->prev->setNext(NULL); + } + //edge->setPrev(NULL); + } + // Disconnect from next. + if (edge->next) { + if (edge->next->prev == edge) { + edge->next->setPrev(NULL); + } + //edge->setNext(NULL); + } + } + + void remove(Edge *edge) { + xaDebugAssert(edge != NULL); + disconnect(edge); + delete edge; + } + + void remove(Vertex *vertex) { + xaDebugAssert(vertex != NULL); + // Remove from vertex list. + m_vertexArray[vertex->id] = NULL; + // Disconnect from colocals. + vertex->unlinkColocal(); + // Disconnect from edges. + if (vertex->edge != NULL) { + // @@ Removing a connected vertex is asking for trouble... + if (vertex->edge->vertex == vertex) { + // @@ Connect edge to a colocal? + vertex->edge->vertex = NULL; + } + vertex->setEdge(NULL); + } + delete vertex; + } + + void remove(Face *face) { + xaDebugAssert(face != NULL); + // Remove from face list. + m_faceArray[face->id] = NULL; + // Disconnect from edges. + if (face->edge != NULL) { + xaDebugAssert(face->edge->face == face); + face->edge->face = NULL; + face->edge = NULL; + } + delete face; + } + + // Triangulate in place. + void triangulate() { + bool all_triangles = true; + const uint32_t faceCount = m_faceArray.size(); + for (uint32_t f = 0; f < faceCount; f++) { + Face *face = m_faceArray[f]; + if (face->edgeCount() != 3) { + all_triangles = false; + break; + } + } + if (all_triangles) { + return; + } + // Do not touch vertices, but rebuild edges and faces. + std::vector<Edge *> edgeArray; + std::vector<Face *> faceArray; + std::swap(edgeArray, m_edgeArray); + std::swap(faceArray, m_faceArray); + m_edgeMap.clear(); + for (uint32_t f = 0; f < faceCount; f++) { + Face *face = faceArray[f]; + // Trivial fan-like triangulation. + const uint32_t v0 = face->edge->vertex->id; + uint32_t v2, v1 = (uint32_t)-1; + for (Face::EdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + Edge *edge = it.current(); + v2 = edge->to()->id; + if (v2 == v0) break; + if (v1 != -1) addFace(v0, v1, v2); + v1 = v2; + } + } + xaDebugAssert(m_faceArray.size() > faceCount); // triangle count > face count + linkBoundary(); + for (size_t i = 0; i < edgeArray.size(); i++) + delete edgeArray[i]; + for (size_t i = 0; i < faceArray.size(); i++) + delete faceArray[i]; + } + + /// Link boundary edges once the mesh has been created. + void linkBoundary() { + xaPrint("--- Linking boundaries:\n"); + int num = 0; + // Create boundary edges. + uint32_t edgeCount = this->edgeCount(); + for (uint32_t e = 0; e < edgeCount; e++) { + Edge *edge = edgeAt(e); + if (edge != NULL && edge->pair == NULL) { + Edge *pair = new Edge(edge->id + 1); + uint32_t i = edge->from()->id; + uint32_t j = edge->next->from()->id; + Key key(j, i); + xaAssert(m_edgeMap.find(key) == m_edgeMap.end()); + pair->vertex = m_vertexArray[j]; + m_edgeMap[key] = pair; + edge->pair = pair; + pair->pair = edge; + num++; + } + } + // Link boundary edges. + for (uint32_t e = 0; e < edgeCount; e++) { + Edge *edge = edgeAt(e); + if (edge != NULL && edge->pair->face == NULL) { + linkBoundaryEdge(edge->pair); + } + } + xaPrint("--- %d boundary edges.\n", num); + } + + /* + Fixing T-junctions. + + - Find T-junctions. Find vertices that are on an edge. + - This test is approximate. + - Insert edges on a spatial index to speedup queries. + - Consider only open edges, that is edges that have no pairs. + - Consider only vertices on boundaries. + - Close T-junction. + - Split edge. + + */ + bool splitBoundaryEdges() // Returns true if any split was made. + { + std::vector<Vertex *> boundaryVertices; + for (uint32_t i = 0; i < m_vertexArray.size(); i++) { + Vertex *v = m_vertexArray[i]; + if (v->isBoundary()) { + boundaryVertices.push_back(v); + } + } + xaPrint("Fixing T-junctions:\n"); + int splitCount = 0; + for (uint32_t v = 0; v < boundaryVertices.size(); v++) { + Vertex *vertex = boundaryVertices[v]; + Vector3 x0 = vertex->pos; + // Find edges that this vertex overlaps with. + for (uint32_t e = 0; e < m_edgeArray.size(); e++) { + Edge *edge = m_edgeArray[e]; + if (edge != NULL && edge->isBoundary()) { + if (edge->from() == vertex || edge->to() == vertex) { + continue; + } + Vector3 x1 = edge->from()->pos; + Vector3 x2 = edge->to()->pos; + Vector3 v01 = x0 - x1; + Vector3 v21 = x2 - x1; + float l = length(v21); + float d = length(cross(v01, v21)) / l; + if (isZero(d)) { + float t = dot(v01, v21) / (l * l); + if (t > 0.0f + NV_EPSILON && t < 1.0f - NV_EPSILON) { + xaDebugAssert(equal(lerp(x1, x2, t), x0)); + Vertex *splitVertex = splitBoundaryEdge(edge, t, x0); + vertex->linkColocal(splitVertex); // @@ Should we do this here? + splitCount++; + } + } + } + } + } + xaPrint(" - %d edges split.\n", splitCount); + xaDebugAssert(isValid()); + return splitCount != 0; + } + + // Vertices + uint32_t vertexCount() const { + return m_vertexArray.size(); + } + const Vertex *vertexAt(int i) const { + return m_vertexArray[i]; + } + Vertex *vertexAt(int i) { + return m_vertexArray[i]; + } + + uint32_t colocalVertexCount() const { + return m_colocalVertexCount; + } + + // Faces + uint32_t faceCount() const { + return m_faceArray.size(); + } + const Face *faceAt(int i) const { + return m_faceArray[i]; + } + Face *faceAt(int i) { + return m_faceArray[i]; + } + + // Edges + uint32_t edgeCount() const { + return m_edgeArray.size(); + } + const Edge *edgeAt(int i) const { + return m_edgeArray[i]; + } + Edge *edgeAt(int i) { + return m_edgeArray[i]; + } + + class ConstVertexIterator; + + class VertexIterator { + friend class ConstVertexIterator; + + public: + VertexIterator(Mesh *mesh) : + m_mesh(mesh), + m_current(0) {} + + virtual void advance() { + m_current++; + } + virtual bool isDone() const { + return m_current == m_mesh->vertexCount(); + } + virtual Vertex *current() const { + return m_mesh->vertexAt(m_current); + } + + private: + halfedge::Mesh *m_mesh; + uint32_t m_current; + }; + VertexIterator vertices() { + return VertexIterator(this); + } + + class ConstVertexIterator { + public: + ConstVertexIterator(const Mesh *mesh) : + m_mesh(mesh), + m_current(0) {} + ConstVertexIterator(class VertexIterator &it) : + m_mesh(it.m_mesh), + m_current(it.m_current) {} + + virtual void advance() { + m_current++; + } + virtual bool isDone() const { + return m_current == m_mesh->vertexCount(); + } + virtual const Vertex *current() const { + return m_mesh->vertexAt(m_current); + } + + private: + const halfedge::Mesh *m_mesh; + uint32_t m_current; + }; + ConstVertexIterator vertices() const { + return ConstVertexIterator(this); + } + + class ConstFaceIterator; + + class FaceIterator { + friend class ConstFaceIterator; + + public: + FaceIterator(Mesh *mesh) : + m_mesh(mesh), + m_current(0) {} + + virtual void advance() { + m_current++; + } + virtual bool isDone() const { + return m_current == m_mesh->faceCount(); + } + virtual Face *current() const { + return m_mesh->faceAt(m_current); + } + + private: + halfedge::Mesh *m_mesh; + uint32_t m_current; + }; + FaceIterator faces() { + return FaceIterator(this); + } + + class ConstFaceIterator { + public: + ConstFaceIterator(const Mesh *mesh) : + m_mesh(mesh), + m_current(0) {} + ConstFaceIterator(const FaceIterator &it) : + m_mesh(it.m_mesh), + m_current(it.m_current) {} + + virtual void advance() { + m_current++; + } + virtual bool isDone() const { + return m_current == m_mesh->faceCount(); + } + virtual const Face *current() const { + return m_mesh->faceAt(m_current); + } + + private: + const halfedge::Mesh *m_mesh; + uint32_t m_current; + }; + ConstFaceIterator faces() const { + return ConstFaceIterator(this); + } + + class ConstEdgeIterator; + + class EdgeIterator { + friend class ConstEdgeIterator; + + public: + EdgeIterator(Mesh *mesh) : + m_mesh(mesh), + m_current(0) {} + + virtual void advance() { + m_current++; + } + virtual bool isDone() const { + return m_current == m_mesh->edgeCount(); + } + virtual Edge *current() const { + return m_mesh->edgeAt(m_current); + } + + private: + halfedge::Mesh *m_mesh; + uint32_t m_current; + }; + EdgeIterator edges() { + return EdgeIterator(this); + } + + class ConstEdgeIterator { + public: + ConstEdgeIterator(const Mesh *mesh) : + m_mesh(mesh), + m_current(0) {} + ConstEdgeIterator(const EdgeIterator &it) : + m_mesh(it.m_mesh), + m_current(it.m_current) {} + + virtual void advance() { + m_current++; + } + virtual bool isDone() const { + return m_current == m_mesh->edgeCount(); + } + virtual const Edge *current() const { + return m_mesh->edgeAt(m_current); + } + + private: + const halfedge::Mesh *m_mesh; + uint32_t m_current; + }; + ConstEdgeIterator edges() const { + return ConstEdgeIterator(this); + } + + // @@ Add half-edge iterator. + + bool isValid() const { + // Make sure all edges are valid. + const uint32_t edgeCount = m_edgeArray.size(); + for (uint32_t e = 0; e < edgeCount; e++) { + Edge *edge = m_edgeArray[e]; + if (edge != NULL) { + if (edge->id != 2 * e) { + return false; + } + if (!edge->isValid()) { + return false; + } + if (edge->pair->id != 2 * e + 1) { + return false; + } + if (!edge->pair->isValid()) { + return false; + } + } + } + // @@ Make sure all faces are valid. + // @@ Make sure all vertices are valid. + return true; + } + + // Error status: + + struct ErrorCode { + enum Enum { + AlreadyAddedEdge, + DegenerateColocalEdge, + DegenerateEdge, + DuplicateEdge + }; + }; + + mutable ErrorCode::Enum errorCode; + mutable uint32_t errorIndex0; + mutable uint32_t errorIndex1; + +private: + // Return true if the face can be added to the manifold mesh. + bool canAddFace(const std::vector<uint32_t> &indexArray, uint32_t first, uint32_t num) const { + return canAddFace(indexArray.data(), first, num); + } + + bool canAddFace(const uint32_t *indexArray, uint32_t first, uint32_t num) const { + for (uint32_t j = num - 1, i = 0; i < num; j = i++) { + if (!canAddEdge(indexArray[first + j], indexArray[first + i])) { + errorIndex0 = indexArray[first + j]; + errorIndex1 = indexArray[first + i]; + return false; + } + } + // We also have to make sure the face does not have any duplicate edge! + for (uint32_t i = 0; i < num; i++) { + int i0 = indexArray[first + i + 0]; + int i1 = indexArray[first + (i + 1) % num]; + for (uint32_t j = i + 1; j < num; j++) { + int j0 = indexArray[first + j + 0]; + int j1 = indexArray[first + (j + 1) % num]; + if (i0 == j0 && i1 == j1) { + errorCode = ErrorCode::DuplicateEdge; + errorIndex0 = i0; + errorIndex1 = i1; + return false; + } + } + } + return true; + } + + // Return true if the edge doesn't exist or doesn't have any adjacent face. + bool canAddEdge(uint32_t i, uint32_t j) const { + if (i == j) { + // Skip degenerate edges. + errorCode = ErrorCode::DegenerateEdge; + return false; + } + // Same check, but taking into account colocal vertices. + const Vertex *v0 = vertexAt(i); + const Vertex *v1 = vertexAt(j); + for (Vertex::ConstVertexIterator it(v0->colocals()); !it.isDone(); it.advance()) { + if (it.current() == v1) { + // Skip degenerate edges. + errorCode = ErrorCode::DegenerateColocalEdge; + return false; + } + } + // Make sure edge has not been added yet. + Edge *edge = findEdge(i, j); + // We ignore edges that don't have an adjacent face yet, since this face could become the edge's face. + if (!(edge == NULL || edge->face == NULL)) { + errorCode = ErrorCode::AlreadyAddedEdge; + return false; + } + return true; + } + + Edge *addEdge(uint32_t i, uint32_t j) { + xaAssert(i != j); + Edge *edge = findEdge(i, j); + if (edge != NULL) { + // Edge may already exist, but its face must not be set. + xaDebugAssert(edge->face == NULL); + // Nothing else to do! + } else { + // Add new edge. + // Lookup pair. + Edge *pair = findEdge(j, i); + if (pair != NULL) { + // Create edge with same id. + edge = new Edge(pair->id + 1); + // Link edge pairs. + edge->pair = pair; + pair->pair = edge; + // @@ I'm not sure this is necessary! + pair->vertex->setEdge(pair); + } else { + // Create edge. + edge = new Edge(2 * m_edgeArray.size()); + // Add only unpaired edges. + m_edgeArray.push_back(edge); + } + edge->vertex = m_vertexArray[i]; + m_edgeMap[Key(i, j)] = edge; + } + // Face and Next are set by addFace. + return edge; + } + + /// Find edge, test all colocals. + Edge *findEdge(uint32_t i, uint32_t j) const { + Edge *edge = NULL; + const Vertex *v0 = vertexAt(i); + const Vertex *v1 = vertexAt(j); + // Test all colocal pairs. + for (Vertex::ConstVertexIterator it0(v0->colocals()); !it0.isDone(); it0.advance()) { + for (Vertex::ConstVertexIterator it1(v1->colocals()); !it1.isDone(); it1.advance()) { + Key key(it0.current()->id, it1.current()->id); + if (edge == NULL) { + auto edgeIt = m_edgeMap.find(key); + if (edgeIt != m_edgeMap.end()) + edge = (*edgeIt).second; +#if !defined(_DEBUG) + if (edge != NULL) return edge; +#endif + } else { + // Make sure that only one edge is found. + xaDebugAssert(m_edgeMap.find(key) == m_edgeMap.end()); + } + } + } + return edge; + } + + /// Link this boundary edge. + void linkBoundaryEdge(Edge *edge) { + xaAssert(edge->face == NULL); + // Make sure next pointer has not been set. @@ We want to be able to relink boundary edges after mesh changes. + Edge *next = edge; + while (next->pair->face != NULL) { + // Get pair prev + Edge *e = next->pair->next; + while (e->next != next->pair) { + e = e->next; + } + next = e; + } + edge->setNext(next->pair); + // Adjust vertex edge, so that it's the boundary edge. (required for isBoundary()) + if (edge->vertex->edge != edge) { + // Multiple boundaries in the same edge. + edge->vertex->edge = edge; + } + } + + Vertex *splitBoundaryEdge(Edge *edge, float t, const Vector3 &pos) { + /* + We want to go from this configuration: + + + + + | ^ + edge |<->| pair + v | + + + + + To this one: + + + + + | ^ + e0 |<->| p0 + v | + vertex + + + | ^ + e1 |<->| p1 + v | + + + + + */ + Edge *pair = edge->pair; + // Make sure boundaries are linked. + xaDebugAssert(pair != NULL); + // Make sure edge is a boundary edge. + xaDebugAssert(pair->face == NULL); + // Add new vertex. + Vertex *vertex = addVertex(pos); + vertex->nor = lerp(edge->from()->nor, edge->to()->nor, t); + vertex->tex = lerp(edge->from()->tex, edge->to()->tex, t); + disconnect(edge); + disconnect(pair); + // Add edges. + Edge *e0 = addEdge(edge->from()->id, vertex->id); + Edge *p0 = addEdge(vertex->id, pair->to()->id); + Edge *e1 = addEdge(vertex->id, edge->to()->id); + Edge *p1 = addEdge(pair->from()->id, vertex->id); + // Link edges. + e0->setNext(e1); + p1->setNext(p0); + e0->setPrev(edge->prev); + e1->setNext(edge->next); + p1->setPrev(pair->prev); + p0->setNext(pair->next); + xaDebugAssert(e0->next == e1); + xaDebugAssert(e1->prev == e0); + xaDebugAssert(p1->next == p0); + xaDebugAssert(p0->prev == p1); + xaDebugAssert(p0->pair == e0); + xaDebugAssert(e0->pair == p0); + xaDebugAssert(p1->pair == e1); + xaDebugAssert(e1->pair == p1); + // Link faces. + e0->face = edge->face; + e1->face = edge->face; + // Link vertices. + edge->from()->setEdge(e0); + vertex->setEdge(e1); + delete edge; + delete pair; + return vertex; + } + +private: + std::vector<Vertex *> m_vertexArray; + std::vector<Edge *> m_edgeArray; + std::vector<Face *> m_faceArray; + + struct Key { + Key() {} + Key(const Key &k) : + p0(k.p0), + p1(k.p1) {} + Key(uint32_t v0, uint32_t v1) : + p0(v0), + p1(v1) {} + void operator=(const Key &k) { + p0 = k.p0; + p1 = k.p1; + } + bool operator==(const Key &k) const { + return p0 == k.p0 && p1 == k.p1; + } + + uint32_t p0; + uint32_t p1; + }; + + friend struct Hash<Mesh::Key>; + std::unordered_map<Key, Edge *, Hash<Key>, Equal<Key> > m_edgeMap; + uint32_t m_colocalVertexCount; +}; + +class MeshTopology { +public: + MeshTopology(const Mesh *mesh) { + buildTopologyInfo(mesh); + } + + /// Determine if the mesh is connected. + bool isConnected() const { + return m_connectedCount == 1; + } + + /// Determine if the mesh is closed. (Each edge is shared by two faces) + bool isClosed() const { + return m_boundaryCount == 0; + } + + /// Return true if the mesh has the topology of a disk. + bool isDisk() const { + return isConnected() && m_boundaryCount == 1 /* && m_eulerNumber == 1*/; + } + +private: + void buildTopologyInfo(const Mesh *mesh) { + const uint32_t vertexCount = mesh->colocalVertexCount(); + const uint32_t faceCount = mesh->faceCount(); + const uint32_t edgeCount = mesh->edgeCount(); + xaPrint("--- Building mesh topology:\n"); + std::vector<uint32_t> stack(faceCount); + BitArray bitFlags(faceCount); + bitFlags.clearAll(); + // Compute connectivity. + xaPrint("--- Computing connectivity.\n"); + m_connectedCount = 0; + for (uint32_t f = 0; f < faceCount; f++) { + if (bitFlags.bitAt(f) == false) { + m_connectedCount++; + stack.push_back(f); + while (!stack.empty()) { + const uint32_t top = stack.back(); + xaAssert(top != uint32_t(~0)); + stack.pop_back(); + if (bitFlags.bitAt(top) == false) { + bitFlags.setBitAt(top); + const Face *face = mesh->faceAt(top); + const Edge *firstEdge = face->edge; + const Edge *edge = firstEdge; + do { + const Face *neighborFace = edge->pair->face; + if (neighborFace != NULL) { + stack.push_back(neighborFace->id); + } + edge = edge->next; + } while (edge != firstEdge); + } + } + } + } + xaAssert(stack.empty()); + xaPrint("--- %d connected components.\n", m_connectedCount); + // Count boundary loops. + xaPrint("--- Counting boundary loops.\n"); + m_boundaryCount = 0; + bitFlags.resize(edgeCount); + bitFlags.clearAll(); + // Don't forget to link the boundary otherwise this won't work. + for (uint32_t e = 0; e < edgeCount; e++) { + const Edge *startEdge = mesh->edgeAt(e); + if (startEdge != NULL && startEdge->isBoundary() && bitFlags.bitAt(e) == false) { + xaDebugAssert(startEdge->face != NULL); + xaDebugAssert(startEdge->pair->face == NULL); + startEdge = startEdge->pair; + m_boundaryCount++; + const Edge *edge = startEdge; + do { + bitFlags.setBitAt(edge->id / 2); + edge = edge->next; + } while (startEdge != edge); + } + } + xaPrint("--- %d boundary loops found.\n", m_boundaryCount); + // Compute euler number. + m_eulerNumber = vertexCount - edgeCount + faceCount; + xaPrint("--- Euler number: %d.\n", m_eulerNumber); + // Compute genus. (only valid on closed connected surfaces) + m_genus = -1; + if (isClosed() && isConnected()) { + m_genus = (2 - m_eulerNumber) / 2; + xaPrint("--- Genus: %d.\n", m_genus); + } + } + +private: + ///< Number of boundary loops. + int m_boundaryCount; + + ///< Number of connected components. + int m_connectedCount; + + ///< Euler number. + int m_eulerNumber; + + /// Mesh genus. + int m_genus; +}; + +float computeSurfaceArea(const halfedge::Mesh *mesh) { + float area = 0; + for (halfedge::Mesh::ConstFaceIterator it(mesh->faces()); !it.isDone(); it.advance()) { + const halfedge::Face *face = it.current(); + area += face->area(); + } + xaDebugAssert(area >= 0); + return area; +} + +float computeParametricArea(const halfedge::Mesh *mesh) { + float area = 0; + for (halfedge::Mesh::ConstFaceIterator it(mesh->faces()); !it.isDone(); it.advance()) { + const halfedge::Face *face = it.current(); + area += face->parametricArea(); + } + return area; +} + +uint32_t countMeshTriangles(const Mesh *mesh) { + const uint32_t faceCount = mesh->faceCount(); + uint32_t triangleCount = 0; + for (uint32_t f = 0; f < faceCount; f++) { + const Face *face = mesh->faceAt(f); + uint32_t edgeCount = face->edgeCount(); + xaDebugAssert(edgeCount > 2); + triangleCount += edgeCount - 2; + } + return triangleCount; +} + +Mesh *unifyVertices(const Mesh *inputMesh) { + Mesh *mesh = new Mesh; + // Only add the first colocal. + const uint32_t vertexCount = inputMesh->vertexCount(); + for (uint32_t v = 0; v < vertexCount; v++) { + const Vertex *vertex = inputMesh->vertexAt(v); + if (vertex->isFirstColocal()) { + mesh->addVertex(vertex->pos); + } + } + std::vector<uint32_t> indexArray; + // Add new faces pointing to first colocals. + uint32_t faceCount = inputMesh->faceCount(); + for (uint32_t f = 0; f < faceCount; f++) { + const Face *face = inputMesh->faceAt(f); + indexArray.clear(); + for (Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const Edge *edge = it.current(); + const Vertex *vertex = edge->vertex->firstColocal(); + indexArray.push_back(vertex->id); + } + mesh->addFace(indexArray); + } + mesh->linkBoundary(); + return mesh; +} + +static bool pointInTriangle(const Vector2 &p, const Vector2 &a, const Vector2 &b, const Vector2 &c) { + return triangleArea(a, b, p) >= 0.00001f && + triangleArea(b, c, p) >= 0.00001f && + triangleArea(c, a, p) >= 0.00001f; +} + +// This is doing a simple ear-clipping algorithm that skips invalid triangles. Ideally, we should +// also sort the ears by angle, start with the ones that have the smallest angle and proceed in order. +Mesh *triangulate(const Mesh *inputMesh) { + Mesh *mesh = new Mesh; + // Add all vertices. + const uint32_t vertexCount = inputMesh->vertexCount(); + for (uint32_t v = 0; v < vertexCount; v++) { + const Vertex *vertex = inputMesh->vertexAt(v); + mesh->addVertex(vertex->pos); + } + std::vector<int> polygonVertices; + std::vector<float> polygonAngles; + std::vector<Vector2> polygonPoints; + const uint32_t faceCount = inputMesh->faceCount(); + for (uint32_t f = 0; f < faceCount; f++) { + const Face *face = inputMesh->faceAt(f); + xaDebugAssert(face != NULL); + const uint32_t edgeCount = face->edgeCount(); + xaDebugAssert(edgeCount >= 3); + polygonVertices.clear(); + polygonVertices.reserve(edgeCount); + if (edgeCount == 3) { + // Simple case for triangles. + for (Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const Edge *edge = it.current(); + const Vertex *vertex = edge->vertex; + polygonVertices.push_back(vertex->id); + } + int v0 = polygonVertices[0]; + int v1 = polygonVertices[1]; + int v2 = polygonVertices[2]; + mesh->addFace(v0, v1, v2); + } else { + // Build 2D polygon projecting vertices onto normal plane. + // Faces are not necesarily planar, this is for example the case, when the face comes from filling a hole. In such cases + // it's much better to use the best fit plane. + const Vector3 fn = face->normal(); + Basis basis; + basis.buildFrameForDirection(fn); + polygonPoints.clear(); + polygonPoints.reserve(edgeCount); + polygonAngles.clear(); + polygonAngles.reserve(edgeCount); + for (Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const Edge *edge = it.current(); + const Vertex *vertex = edge->vertex; + polygonVertices.push_back(vertex->id); + Vector2 p; + p.x = dot(basis.tangent, vertex->pos); + p.y = dot(basis.bitangent, vertex->pos); + polygonPoints.push_back(p); + } + polygonAngles.resize(edgeCount); + while (polygonVertices.size() > 2) { + uint32_t size = polygonVertices.size(); + // Update polygon angles. @@ Update only those that have changed. + float minAngle = 2 * PI; + uint32_t bestEar = 0; // Use first one if none of them is valid. + bool bestIsValid = false; + for (uint32_t i = 0; i < size; i++) { + uint32_t i0 = i; + uint32_t i1 = (i + 1) % size; // Use Sean's polygon interation trick. + uint32_t i2 = (i + 2) % size; + Vector2 p0 = polygonPoints[i0]; + Vector2 p1 = polygonPoints[i1]; + Vector2 p2 = polygonPoints[i2]; + + bool degenerate = distance(p0, p1) < NV_EPSILON || distance(p0, p2) < NV_EPSILON || distance(p1, p2) < NV_EPSILON; + if (degenerate) { + continue; + } + + float d = clamp(dot(p0 - p1, p2 - p1) / (length(p0 - p1) * length(p2 - p1)), -1.0f, 1.0f); + float angle = acosf(d); + float area = triangleArea(p0, p1, p2); + if (area < 0.0f) angle = 2.0f * PI - angle; + polygonAngles[i1] = angle; + if (angle < minAngle || !bestIsValid) { + // Make sure this is a valid ear, if not, skip this point. + bool valid = true; + for (uint32_t j = 0; j < size; j++) { + if (j == i0 || j == i1 || j == i2) continue; + Vector2 p = polygonPoints[j]; + if (pointInTriangle(p, p0, p1, p2)) { + valid = false; + break; + } + } + if (valid || !bestIsValid) { + minAngle = angle; + bestEar = i1; + bestIsValid = valid; + } + } + } + if (!bestIsValid) + break; + + xaDebugAssert(minAngle <= 2 * PI); + // Clip best ear: + uint32_t i0 = (bestEar + size - 1) % size; + uint32_t i1 = (bestEar + 0) % size; + uint32_t i2 = (bestEar + 1) % size; + int v0 = polygonVertices[i0]; + int v1 = polygonVertices[i1]; + int v2 = polygonVertices[i2]; + mesh->addFace(v0, v1, v2); + polygonVertices.erase(polygonVertices.begin() + i1); + polygonPoints.erase(polygonPoints.begin() + i1); + polygonAngles.erase(polygonAngles.begin() + i1); + } + } + } + mesh->linkBoundary(); + return mesh; +} + +} // namespace halfedge + +/// Mersenne twister random number generator. +class MTRand { +public: + enum time_e { Time }; + enum { N = 624 }; // length of state vector + enum { M = 397 }; + + /// Constructor that uses the current time as the seed. + MTRand(time_e) { + seed((uint32_t)time(NULL)); + } + + /// Constructor that uses the given seed. + MTRand(uint32_t s = 0) { + seed(s); + } + + /// Provide a new seed. + void seed(uint32_t s) { + initialize(s); + reload(); + } + + /// Get a random number between 0 - 65536. + uint32_t get() { + // Pull a 32-bit integer from the generator state + // Every other access function simply transforms the numbers extracted here + if (left == 0) { + reload(); + } + left--; + uint32_t s1; + s1 = *next++; + s1 ^= (s1 >> 11); + s1 ^= (s1 << 7) & 0x9d2c5680U; + s1 ^= (s1 << 15) & 0xefc60000U; + return (s1 ^ (s1 >> 18)); + }; + + /// Get a random number on [0, max] interval. + uint32_t getRange(uint32_t max) { + if (max == 0) return 0; + if (max == NV_UINT32_MAX) return get(); + const uint32_t np2 = nextPowerOfTwo(max + 1); // @@ This fails if max == NV_UINT32_MAX + const uint32_t mask = np2 - 1; + uint32_t n; + do { + n = get() & mask; + } while (n > max); + return n; + } + +private: + void initialize(uint32_t seed) { + // Initialize generator state with seed + // See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier. + // In previous versions, most significant bits (MSBs) of the seed affect + // only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. + uint32_t *s = state; + uint32_t *r = state; + int i = 1; + *s++ = seed & 0xffffffffUL; + for (; i < N; ++i) { + *s++ = (1812433253UL * (*r ^ (*r >> 30)) + i) & 0xffffffffUL; + r++; + } + } + + void reload() { + // Generate N new values in state + // Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) + uint32_t *p = state; + int i; + for (i = N - M; i--; ++p) + *p = twist(p[M], p[0], p[1]); + for (i = M; --i; ++p) + *p = twist(p[M - N], p[0], p[1]); + *p = twist(p[M - N], p[0], state[0]); + left = N, next = state; + } + + uint32_t hiBit(uint32_t u) const { + return u & 0x80000000U; + } + uint32_t loBit(uint32_t u) const { + return u & 0x00000001U; + } + uint32_t loBits(uint32_t u) const { + return u & 0x7fffffffU; + } + uint32_t mixBits(uint32_t u, uint32_t v) const { + return hiBit(u) | loBits(v); + } + uint32_t twist(uint32_t m, uint32_t s0, uint32_t s1) const { + return m ^ (mixBits(s0, s1) >> 1) ^ ((~loBit(s1) + 1) & 0x9908b0dfU); + } + + uint32_t state[N]; // internal state + uint32_t *next; // next value to get from state + int left; // number of values left before reload needed +}; + +namespace morton { +// Code from ryg: +// http://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/ + +// Inverse of part1By1 - "delete" all odd-indexed bits +uint32_t compact1By1(uint32_t x) { + x &= 0x55555555; // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0 + x = (x ^ (x >> 1)) & 0x33333333; // x = --fe --dc --ba --98 --76 --54 --32 --10 + x = (x ^ (x >> 2)) & 0x0f0f0f0f; // x = ---- fedc ---- ba98 ---- 7654 ---- 3210 + x = (x ^ (x >> 4)) & 0x00ff00ff; // x = ---- ---- fedc ba98 ---- ---- 7654 3210 + x = (x ^ (x >> 8)) & 0x0000ffff; // x = ---- ---- ---- ---- fedc ba98 7654 3210 + return x; +} + +// Inverse of part1By2 - "delete" all bits not at positions divisible by 3 +uint32_t compact1By2(uint32_t x) { + x &= 0x09249249; // x = ---- 9--8 --7- -6-- 5--4 --3- -2-- 1--0 + x = (x ^ (x >> 2)) & 0x030c30c3; // x = ---- --98 ---- 76-- --54 ---- 32-- --10 + x = (x ^ (x >> 4)) & 0x0300f00f; // x = ---- --98 ---- ---- 7654 ---- ---- 3210 + x = (x ^ (x >> 8)) & 0xff0000ff; // x = ---- --98 ---- ---- ---- ---- 7654 3210 + x = (x ^ (x >> 16)) & 0x000003ff; // x = ---- ---- ---- ---- ---- --98 7654 3210 + return x; +} + +uint32_t decodeMorton2X(uint32_t code) { + return compact1By1(code >> 0); +} + +uint32_t decodeMorton2Y(uint32_t code) { + return compact1By1(code >> 1); +} + +uint32_t decodeMorton3X(uint32_t code) { + return compact1By2(code >> 0); +} + +uint32_t decodeMorton3Y(uint32_t code) { + return compact1By2(code >> 1); +} + +uint32_t decodeMorton3Z(uint32_t code) { + return compact1By2(code >> 2); +} +} // namespace morton + +// A simple, dynamic proximity grid based on Jon's code. +// Instead of storing pointers here I store indices. +struct ProximityGrid { + void init(const Box &box, uint32_t count) { + cellArray.clear(); + // Determine grid size. + float cellWidth; + Vector3 diagonal = box.extents() * 2.f; + float volume = box.volume(); + if (equal(volume, 0)) { + // Degenerate box, treat like a quad. + Vector2 quad; + if (diagonal.x < diagonal.y && diagonal.x < diagonal.z) { + quad.x = diagonal.y; + quad.y = diagonal.z; + } else if (diagonal.y < diagonal.x && diagonal.y < diagonal.z) { + quad.x = diagonal.x; + quad.y = diagonal.z; + } else { + quad.x = diagonal.x; + quad.y = diagonal.y; + } + float cellArea = quad.x * quad.y / count; + cellWidth = sqrtf(cellArea); // pow(cellArea, 1.0f / 2.0f); + } else { + // Ideally we want one cell per point. + float cellVolume = volume / count; + cellWidth = powf(cellVolume, 1.0f / 3.0f); + } + xaDebugAssert(cellWidth != 0); + sx = std::max(1, ftoi_ceil(diagonal.x / cellWidth)); + sy = std::max(1, ftoi_ceil(diagonal.y / cellWidth)); + sz = std::max(1, ftoi_ceil(diagonal.z / cellWidth)); + invCellSize.x = float(sx) / diagonal.x; + invCellSize.y = float(sy) / diagonal.y; + invCellSize.z = float(sz) / diagonal.z; + cellArray.resize(sx * sy * sz); + corner = box.minCorner; // @@ Align grid better? + } + + int index_x(float x) const { + return clamp(ftoi_floor((x - corner.x) * invCellSize.x), 0, sx - 1); + } + + int index_y(float y) const { + return clamp(ftoi_floor((y - corner.y) * invCellSize.y), 0, sy - 1); + } + + int index_z(float z) const { + return clamp(ftoi_floor((z - corner.z) * invCellSize.z), 0, sz - 1); + } + + int index(int x, int y, int z) const { + xaDebugAssert(x >= 0 && x < sx); + xaDebugAssert(y >= 0 && y < sy); + xaDebugAssert(z >= 0 && z < sz); + int idx = (z * sy + y) * sx + x; + xaDebugAssert(idx >= 0 && uint32_t(idx) < cellArray.size()); + return idx; + } + + uint32_t mortonCount() const { + uint64_t s = uint64_t(max3(sx, sy, sz)); + s = nextPowerOfTwo(s); + if (s > 1024) { + return uint32_t(s * s * min3(sx, sy, sz)); + } + return uint32_t(s * s * s); + } + + int mortonIndex(uint32_t code) const { + uint32_t x, y, z; + uint32_t s = uint32_t(max3(sx, sy, sz)); + if (s > 1024) { + // Use layered two-dimensional morton order. + s = nextPowerOfTwo(s); + uint32_t layer = code / (s * s); + code = code % (s * s); + uint32_t layer_count = uint32_t(min3(sx, sy, sz)); + if (sx == (int)layer_count) { + x = layer; + y = morton::decodeMorton2X(code); + z = morton::decodeMorton2Y(code); + } else if (sy == (int)layer_count) { + x = morton::decodeMorton2Y(code); + y = layer; + z = morton::decodeMorton2X(code); + } else { /*if (sz == layer_count)*/ + x = morton::decodeMorton2X(code); + y = morton::decodeMorton2Y(code); + z = layer; + } + } else { + x = morton::decodeMorton3X(code); + y = morton::decodeMorton3Y(code); + z = morton::decodeMorton3Z(code); + } + if (x >= uint32_t(sx) || y >= uint32_t(sy) || z >= uint32_t(sz)) { + return -1; + } + return index(x, y, z); + } + + void add(const Vector3 &pos, uint32_t key) { + int x = index_x(pos.x); + int y = index_y(pos.y); + int z = index_z(pos.z); + uint32_t idx = index(x, y, z); + cellArray[idx].indexArray.push_back(key); + } + + // Gather all points inside the given sphere. + // Radius is assumed to be small, so we don't bother culling the cells. + void gather(const Vector3 &position, float radius, std::vector<uint32_t> &indexArray) { + int x0 = index_x(position.x - radius); + int x1 = index_x(position.x + radius); + int y0 = index_y(position.y - radius); + int y1 = index_y(position.y + radius); + int z0 = index_z(position.z - radius); + int z1 = index_z(position.z + radius); + for (int z = z0; z <= z1; z++) { + for (int y = y0; y <= y1; y++) { + for (int x = x0; x <= x1; x++) { + int idx = index(x, y, z); + indexArray.insert(indexArray.begin(), cellArray[idx].indexArray.begin(), cellArray[idx].indexArray.end()); + } + } + } + } + + struct Cell { + std::vector<uint32_t> indexArray; + }; + + std::vector<Cell> cellArray; + + Vector3 corner; + Vector3 invCellSize; + int sx, sy, sz; +}; + +// Based on Pierre Terdiman's and Michael Herf's source code. +// http://www.codercorner.com/RadixSortRevisited.htm +// http://www.stereopsis.com/radix.html +class RadixSort { +public: + RadixSort() : + m_size(0), + m_ranks(NULL), + m_ranks2(NULL), + m_validRanks(false) {} + ~RadixSort() { + // Release everything + free(m_ranks2); + free(m_ranks); + } + + RadixSort &sort(const float *input, uint32_t count) { + if (input == NULL || count == 0) return *this; + // Resize lists if needed + if (count != m_size) { + if (count > m_size) { + m_ranks2 = (uint32_t *)realloc(m_ranks2, sizeof(uint32_t) * count); + m_ranks = (uint32_t *)realloc(m_ranks, sizeof(uint32_t) * count); + } + m_size = count; + m_validRanks = false; + } + if (count < 32) { + insertionSort(input, count); + } else { + // @@ Avoid touching the input multiple times. + for (uint32_t i = 0; i < count; i++) { + FloatFlip((uint32_t &)input[i]); + } + radixSort<uint32_t>((const uint32_t *)input, count); + for (uint32_t i = 0; i < count; i++) { + IFloatFlip((uint32_t &)input[i]); + } + } + return *this; + } + + RadixSort &sort(const std::vector<float> &input) { + return sort(input.data(), input.size()); + } + + // Access to results. m_ranks is a list of indices in sorted order, i.e. in the order you may further process your data + const uint32_t *ranks() const { + xaDebugAssert(m_validRanks); + return m_ranks; + } + uint32_t *ranks() { + xaDebugAssert(m_validRanks); + return m_ranks; + } + +private: + uint32_t m_size; + uint32_t *m_ranks; + uint32_t *m_ranks2; + bool m_validRanks; + + void FloatFlip(uint32_t &f) { + int32_t mask = (int32_t(f) >> 31) | 0x80000000; // Warren Hunt, Manchor Ko. + f ^= mask; + } + + void IFloatFlip(uint32_t &f) { + uint32_t mask = ((f >> 31) - 1) | 0x80000000; // Michael Herf. + f ^= mask; + } + + template <typename T> + void createHistograms(const T *buffer, uint32_t count, uint32_t *histogram) { + const uint32_t bucketCount = sizeof(T); // (8 * sizeof(T)) / log2(radix) + // Init bucket pointers. + uint32_t *h[bucketCount]; + for (uint32_t i = 0; i < bucketCount; i++) { + h[i] = histogram + 256 * i; + } + // Clear histograms. + memset(histogram, 0, 256 * bucketCount * sizeof(uint32_t)); + // @@ Add support for signed integers. + // Build histograms. + const uint8_t *p = (const uint8_t *)buffer; // @@ Does this break aliasing rules? + const uint8_t *pe = p + count * sizeof(T); + while (p != pe) { + h[0][*p++]++, h[1][*p++]++, h[2][*p++]++, h[3][*p++]++; +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4127) +#endif + if (bucketCount == 8) h[4][*p++]++, h[5][*p++]++, h[6][*p++]++, h[7][*p++]++; +#ifdef _MSC_VER +#pragma warning(pop) +#endif + } + } + + template <typename T> + void insertionSort(const T *input, uint32_t count) { + if (!m_validRanks) { + m_ranks[0] = 0; + for (uint32_t i = 1; i != count; ++i) { + int rank = m_ranks[i] = i; + uint32_t j = i; + while (j != 0 && input[rank] < input[m_ranks[j - 1]]) { + m_ranks[j] = m_ranks[j - 1]; + --j; + } + if (i != j) { + m_ranks[j] = rank; + } + } + m_validRanks = true; + } else { + for (uint32_t i = 1; i != count; ++i) { + int rank = m_ranks[i]; + uint32_t j = i; + while (j != 0 && input[rank] < input[m_ranks[j - 1]]) { + m_ranks[j] = m_ranks[j - 1]; + --j; + } + if (i != j) { + m_ranks[j] = rank; + } + } + } + } + + template <typename T> + void radixSort(const T *input, uint32_t count) { + const uint32_t P = sizeof(T); // pass count + // Allocate histograms & offsets on the stack + uint32_t histogram[256 * P]; + uint32_t *link[256]; + createHistograms(input, count, histogram); + // Radix sort, j is the pass number (0=LSB, P=MSB) + for (uint32_t j = 0; j < P; j++) { + // Pointer to this bucket. + const uint32_t *h = &histogram[j * 256]; + const uint8_t *inputBytes = (const uint8_t *)input; // @@ Is this aliasing legal? + inputBytes += j; + if (h[inputBytes[0]] == count) { + // Skip this pass, all values are the same. + continue; + } + // Create offsets + link[0] = m_ranks2; + for (uint32_t i = 1; i < 256; i++) + link[i] = link[i - 1] + h[i - 1]; + // Perform Radix Sort + if (!m_validRanks) { + for (uint32_t i = 0; i < count; i++) { + *link[inputBytes[i * P]]++ = i; + } + m_validRanks = true; + } else { + for (uint32_t i = 0; i < count; i++) { + const uint32_t idx = m_ranks[i]; + *link[inputBytes[idx * P]]++ = idx; + } + } + // Swap pointers for next pass. Valid indices - the most recent ones - are in m_ranks after the swap. + std::swap(m_ranks, m_ranks2); + } + // All values were equal, generate linear ranks. + if (!m_validRanks) { + for (uint32_t i = 0; i < count; i++) { + m_ranks[i] = i; + } + m_validRanks = true; + } + } +}; + +namespace raster { +class ClippedTriangle { +public: + ClippedTriangle(Vector2::Arg a, Vector2::Arg b, Vector2::Arg c) { + m_numVertices = 3; + m_activeVertexBuffer = 0; + m_verticesA[0] = a; + m_verticesA[1] = b; + m_verticesA[2] = c; + m_vertexBuffers[0] = m_verticesA; + m_vertexBuffers[1] = m_verticesB; + } + + uint32_t vertexCount() { + return m_numVertices; + } + + const Vector2 *vertices() { + return m_vertexBuffers[m_activeVertexBuffer]; + } + + void clipHorizontalPlane(float offset, float clipdirection) { + Vector2 *v = m_vertexBuffers[m_activeVertexBuffer]; + m_activeVertexBuffer ^= 1; + Vector2 *v2 = m_vertexBuffers[m_activeVertexBuffer]; + v[m_numVertices] = v[0]; + float dy2, dy1 = offset - v[0].y; + int dy2in, dy1in = clipdirection * dy1 >= 0; + uint32_t p = 0; + for (uint32_t k = 0; k < m_numVertices; k++) { + dy2 = offset - v[k + 1].y; + dy2in = clipdirection * dy2 >= 0; + if (dy1in) v2[p++] = v[k]; + if (dy1in + dy2in == 1) { // not both in/out + float dx = v[k + 1].x - v[k].x; + float dy = v[k + 1].y - v[k].y; + v2[p++] = Vector2(v[k].x + dy1 * (dx / dy), offset); + } + dy1 = dy2; + dy1in = dy2in; + } + m_numVertices = p; + //for (uint32_t k=0; k<m_numVertices; k++) printf("(%f, %f)\n", v2[k].x, v2[k].y); printf("\n"); + } + + void clipVerticalPlane(float offset, float clipdirection) { + Vector2 *v = m_vertexBuffers[m_activeVertexBuffer]; + m_activeVertexBuffer ^= 1; + Vector2 *v2 = m_vertexBuffers[m_activeVertexBuffer]; + v[m_numVertices] = v[0]; + float dx2, dx1 = offset - v[0].x; + int dx2in, dx1in = clipdirection * dx1 >= 0; + uint32_t p = 0; + for (uint32_t k = 0; k < m_numVertices; k++) { + dx2 = offset - v[k + 1].x; + dx2in = clipdirection * dx2 >= 0; + if (dx1in) v2[p++] = v[k]; + if (dx1in + dx2in == 1) { // not both in/out + float dx = v[k + 1].x - v[k].x; + float dy = v[k + 1].y - v[k].y; + v2[p++] = Vector2(offset, v[k].y + dx1 * (dy / dx)); + } + dx1 = dx2; + dx1in = dx2in; + } + m_numVertices = p; + } + + void computeAreaCentroid() { + Vector2 *v = m_vertexBuffers[m_activeVertexBuffer]; + v[m_numVertices] = v[0]; + m_area = 0; + float centroidx = 0, centroidy = 0; + for (uint32_t k = 0; k < m_numVertices; k++) { + // http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/ + float f = v[k].x * v[k + 1].y - v[k + 1].x * v[k].y; + m_area += f; + centroidx += f * (v[k].x + v[k + 1].x); + centroidy += f * (v[k].y + v[k + 1].y); + } + m_area = 0.5f * fabsf(m_area); + if (m_area == 0) { + m_centroid = Vector2(0.0f); + } else { + m_centroid = Vector2(centroidx / (6 * m_area), centroidy / (6 * m_area)); + } + } + + void clipAABox(float x0, float y0, float x1, float y1) { + clipVerticalPlane(x0, -1); + clipHorizontalPlane(y0, -1); + clipVerticalPlane(x1, 1); + clipHorizontalPlane(y1, 1); + computeAreaCentroid(); + } + + Vector2 centroid() { + return m_centroid; + } + + float area() { + return m_area; + } + +private: + Vector2 m_verticesA[7 + 1]; + Vector2 m_verticesB[7 + 1]; + Vector2 *m_vertexBuffers[2]; + uint32_t m_numVertices; + uint32_t m_activeVertexBuffer; + float m_area; + Vector2 m_centroid; +}; + +/// A callback to sample the environment. Return false to terminate rasterization. +typedef bool (*SamplingCallback)(void *param, int x, int y, Vector3::Arg bar, Vector3::Arg dx, Vector3::Arg dy, float coverage); + +/// A triangle for rasterization. +struct Triangle { + Triangle(Vector2::Arg v0, Vector2::Arg v1, Vector2::Arg v2, Vector3::Arg t0, Vector3::Arg t1, Vector3::Arg t2) { + // Init vertices. + this->v1 = v0; + this->v2 = v2; + this->v3 = v1; + // Set barycentric coordinates. + this->t1 = t0; + this->t2 = t2; + this->t3 = t1; + // make sure every triangle is front facing. + flipBackface(); + // Compute deltas. + valid = computeDeltas(); + computeUnitInwardNormals(); + } + + /// Compute texture space deltas. + /// This method takes two edge vectors that form a basis, determines the + /// coordinates of the canonic vectors in that basis, and computes the + /// texture gradient that corresponds to those vectors. + bool computeDeltas() { + Vector2 e0 = v3 - v1; + Vector2 e1 = v2 - v1; + Vector3 de0 = t3 - t1; + Vector3 de1 = t2 - t1; + float denom = 1.0f / (e0.y * e1.x - e1.y * e0.x); + if (!std::isfinite(denom)) { + return false; + } + float lambda1 = -e1.y * denom; + float lambda2 = e0.y * denom; + float lambda3 = e1.x * denom; + float lambda4 = -e0.x * denom; + dx = de0 * lambda1 + de1 * lambda2; + dy = de0 * lambda3 + de1 * lambda4; + return true; + } + + bool draw(const Vector2 &extents, bool enableScissors, SamplingCallback cb, void *param) { + // 28.4 fixed-point coordinates + const int Y1 = ftoi_round(16.0f * v1.y); + const int Y2 = ftoi_round(16.0f * v2.y); + const int Y3 = ftoi_round(16.0f * v3.y); + const int X1 = ftoi_round(16.0f * v1.x); + const int X2 = ftoi_round(16.0f * v2.x); + const int X3 = ftoi_round(16.0f * v3.x); + // Deltas + const int DX12 = X1 - X2; + const int DX23 = X2 - X3; + const int DX31 = X3 - X1; + const int DY12 = Y1 - Y2; + const int DY23 = Y2 - Y3; + const int DY31 = Y3 - Y1; + // Fixed-point deltas + const int FDX12 = DX12 << 4; + const int FDX23 = DX23 << 4; + const int FDX31 = DX31 << 4; + const int FDY12 = DY12 << 4; + const int FDY23 = DY23 << 4; + const int FDY31 = DY31 << 4; + int minx, miny, maxx, maxy; + if (enableScissors) { + int frustumX0 = 0 << 4; + int frustumY0 = 0 << 4; + int frustumX1 = (int)extents.x << 4; + int frustumY1 = (int)extents.y << 4; + // Bounding rectangle + minx = (std::max(min3(X1, X2, X3), frustumX0) + 0xF) >> 4; + miny = (std::max(min3(Y1, Y2, Y3), frustumY0) + 0xF) >> 4; + maxx = (std::min(max3(X1, X2, X3), frustumX1) + 0xF) >> 4; + maxy = (std::min(max3(Y1, Y2, Y3), frustumY1) + 0xF) >> 4; + } else { + // Bounding rectangle + minx = (min3(X1, X2, X3) + 0xF) >> 4; + miny = (min3(Y1, Y2, Y3) + 0xF) >> 4; + maxx = (max3(X1, X2, X3) + 0xF) >> 4; + maxy = (max3(Y1, Y2, Y3) + 0xF) >> 4; + } + // Block size, standard 8x8 (must be power of two) + const int q = 8; + // @@ This won't work when minx,miny are negative. This code path is not used. Leaving as is for now. + xaAssert(minx >= 0); + xaAssert(miny >= 0); + // Start in corner of 8x8 block + minx &= ~(q - 1); + miny &= ~(q - 1); + // Half-edge constants + int C1 = DY12 * X1 - DX12 * Y1; + int C2 = DY23 * X2 - DX23 * Y2; + int C3 = DY31 * X3 - DX31 * Y3; + // Correct for fill convention + if (DY12 < 0 || (DY12 == 0 && DX12 > 0)) C1++; + if (DY23 < 0 || (DY23 == 0 && DX23 > 0)) C2++; + if (DY31 < 0 || (DY31 == 0 && DX31 > 0)) C3++; + // Loop through blocks + for (int y = miny; y < maxy; y += q) { + for (int x = minx; x < maxx; x += q) { + // Corners of block + int x0 = x << 4; + int x1 = (x + q - 1) << 4; + int y0 = y << 4; + int y1 = (y + q - 1) << 4; + // Evaluate half-space functions + bool a00 = C1 + DX12 * y0 - DY12 * x0 > 0; + bool a10 = C1 + DX12 * y0 - DY12 * x1 > 0; + bool a01 = C1 + DX12 * y1 - DY12 * x0 > 0; + bool a11 = C1 + DX12 * y1 - DY12 * x1 > 0; + int a = (a00 << 0) | (a10 << 1) | (a01 << 2) | (a11 << 3); + bool b00 = C2 + DX23 * y0 - DY23 * x0 > 0; + bool b10 = C2 + DX23 * y0 - DY23 * x1 > 0; + bool b01 = C2 + DX23 * y1 - DY23 * x0 > 0; + bool b11 = C2 + DX23 * y1 - DY23 * x1 > 0; + int b = (b00 << 0) | (b10 << 1) | (b01 << 2) | (b11 << 3); + bool c00 = C3 + DX31 * y0 - DY31 * x0 > 0; + bool c10 = C3 + DX31 * y0 - DY31 * x1 > 0; + bool c01 = C3 + DX31 * y1 - DY31 * x0 > 0; + bool c11 = C3 + DX31 * y1 - DY31 * x1 > 0; + int c = (c00 << 0) | (c10 << 1) | (c01 << 2) | (c11 << 3); + // Skip block when outside an edge + if (a == 0x0 || b == 0x0 || c == 0x0) continue; + // Accept whole block when totally covered + if (a == 0xF && b == 0xF && c == 0xF) { + Vector3 texRow = t1 + dy * (y0 - v1.y) + dx * (x0 - v1.x); + for (int iy = y; iy < y + q; iy++) { + Vector3 tex = texRow; + for (int ix = x; ix < x + q; ix++) { + //Vector3 tex = t1 + dx * (ix - v1.x) + dy * (iy - v1.y); + if (!cb(param, ix, iy, tex, dx, dy, 1.0)) { + // early out. + return false; + } + tex += dx; + } + texRow += dy; + } + } else { // Partially covered block + int CY1 = C1 + DX12 * y0 - DY12 * x0; + int CY2 = C2 + DX23 * y0 - DY23 * x0; + int CY3 = C3 + DX31 * y0 - DY31 * x0; + Vector3 texRow = t1 + dy * (y0 - v1.y) + dx * (x0 - v1.x); + for (int iy = y; iy < y + q; iy++) { + int CX1 = CY1; + int CX2 = CY2; + int CX3 = CY3; + Vector3 tex = texRow; + for (int ix = x; ix < x + q; ix++) { + if (CX1 > 0 && CX2 > 0 && CX3 > 0) { + if (!cb(param, ix, iy, tex, dx, dy, 1.0)) { + // early out. + return false; + } + } + CX1 -= FDY12; + CX2 -= FDY23; + CX3 -= FDY31; + tex += dx; + } + CY1 += FDX12; + CY2 += FDX23; + CY3 += FDX31; + texRow += dy; + } + } + } + } + return true; + } + + // extents has to be multiple of BK_SIZE!! + bool drawAA(const Vector2 &extents, bool enableScissors, SamplingCallback cb, void *param) { + const float PX_INSIDE = 1.0f / sqrt(2.0f); + const float PX_OUTSIDE = -1.0f / sqrt(2.0f); + const float BK_SIZE = 8; + const float BK_INSIDE = sqrt(BK_SIZE * BK_SIZE / 2.0f); + const float BK_OUTSIDE = -sqrt(BK_SIZE * BK_SIZE / 2.0f); + + float minx, miny, maxx, maxy; + if (enableScissors) { + // Bounding rectangle + minx = floorf(std::max(min3(v1.x, v2.x, v3.x), 0.0f)); + miny = floorf(std::max(min3(v1.y, v2.y, v3.y), 0.0f)); + maxx = ceilf(std::min(max3(v1.x, v2.x, v3.x), extents.x - 1.0f)); + maxy = ceilf(std::min(max3(v1.y, v2.y, v3.y), extents.y - 1.0f)); + } else { + // Bounding rectangle + minx = floorf(min3(v1.x, v2.x, v3.x)); + miny = floorf(min3(v1.y, v2.y, v3.y)); + maxx = ceilf(max3(v1.x, v2.x, v3.x)); + maxy = ceilf(max3(v1.y, v2.y, v3.y)); + } + // There's no reason to align the blocks to the viewport, instead we align them to the origin of the triangle bounds. + minx = floorf(minx); + miny = floorf(miny); + //minx = (float)(((int)minx) & (~((int)BK_SIZE - 1))); // align to blocksize (we don't need to worry about blocks partially out of viewport) + //miny = (float)(((int)miny) & (~((int)BK_SIZE - 1))); + minx += 0.5; + miny += 0.5; // sampling at texel centers! + maxx += 0.5; + maxy += 0.5; + // Half-edge constants + float C1 = n1.x * (-v1.x) + n1.y * (-v1.y); + float C2 = n2.x * (-v2.x) + n2.y * (-v2.y); + float C3 = n3.x * (-v3.x) + n3.y * (-v3.y); + // Loop through blocks + for (float y0 = miny; y0 <= maxy; y0 += BK_SIZE) { + for (float x0 = minx; x0 <= maxx; x0 += BK_SIZE) { + // Corners of block + float xc = (x0 + (BK_SIZE - 1) / 2.0f); + float yc = (y0 + (BK_SIZE - 1) / 2.0f); + // Evaluate half-space functions + float aC = C1 + n1.x * xc + n1.y * yc; + float bC = C2 + n2.x * xc + n2.y * yc; + float cC = C3 + n3.x * xc + n3.y * yc; + // Skip block when outside an edge + if ((aC <= BK_OUTSIDE) || (bC <= BK_OUTSIDE) || (cC <= BK_OUTSIDE)) continue; + // Accept whole block when totally covered + if ((aC >= BK_INSIDE) && (bC >= BK_INSIDE) && (cC >= BK_INSIDE)) { + Vector3 texRow = t1 + dy * (y0 - v1.y) + dx * (x0 - v1.x); + for (float y = y0; y < y0 + BK_SIZE; y++) { + Vector3 tex = texRow; + for (float x = x0; x < x0 + BK_SIZE; x++) { + if (!cb(param, (int)x, (int)y, tex, dx, dy, 1.0f)) { + return false; + } + tex += dx; + } + texRow += dy; + } + } else { // Partially covered block + float CY1 = C1 + n1.x * x0 + n1.y * y0; + float CY2 = C2 + n2.x * x0 + n2.y * y0; + float CY3 = C3 + n3.x * x0 + n3.y * y0; + Vector3 texRow = t1 + dy * (y0 - v1.y) + dx * (x0 - v1.x); + for (float y = y0; y < y0 + BK_SIZE; y++) { // @@ This is not clipping to scissor rectangle correctly. + float CX1 = CY1; + float CX2 = CY2; + float CX3 = CY3; + Vector3 tex = texRow; + for (float x = x0; x < x0 + BK_SIZE; x++) { // @@ This is not clipping to scissor rectangle correctly. + if (CX1 >= PX_INSIDE && CX2 >= PX_INSIDE && CX3 >= PX_INSIDE) { + // pixel completely covered + Vector3 tex2 = t1 + dx * (x - v1.x) + dy * (y - v1.y); + if (!cb(param, (int)x, (int)y, tex2, dx, dy, 1.0f)) { + return false; + } + } else if ((CX1 >= PX_OUTSIDE) && (CX2 >= PX_OUTSIDE) && (CX3 >= PX_OUTSIDE)) { + // triangle partially covers pixel. do clipping. + ClippedTriangle ct(v1 - Vector2(x, y), v2 - Vector2(x, y), v3 - Vector2(x, y)); + ct.clipAABox(-0.5, -0.5, 0.5, 0.5); + Vector2 centroid = ct.centroid(); + float area = ct.area(); + if (area > 0.0f) { + Vector3 texCent = tex - dx * centroid.x - dy * centroid.y; + //xaAssert(texCent.x >= -0.1f && texCent.x <= 1.1f); // @@ Centroid is not very exact... + //xaAssert(texCent.y >= -0.1f && texCent.y <= 1.1f); + //xaAssert(texCent.z >= -0.1f && texCent.z <= 1.1f); + //Vector3 texCent2 = t1 + dx * (x - v1.x) + dy * (y - v1.y); + if (!cb(param, (int)x, (int)y, texCent, dx, dy, area)) { + return false; + } + } + } + CX1 += n1.x; + CX2 += n2.x; + CX3 += n3.x; + tex += dx; + } + CY1 += n1.y; + CY2 += n2.y; + CY3 += n3.y; + texRow += dy; + } + } + } + } + return true; + } + + void flipBackface() { + // check if triangle is backfacing, if so, swap two vertices + if (((v3.x - v1.x) * (v2.y - v1.y) - (v3.y - v1.y) * (v2.x - v1.x)) < 0) { + Vector2 hv = v1; + v1 = v2; + v2 = hv; // swap pos + Vector3 ht = t1; + t1 = t2; + t2 = ht; // swap tex + } + } + + // compute unit inward normals for each edge. + void computeUnitInwardNormals() { + n1 = v1 - v2; + n1 = Vector2(-n1.y, n1.x); + n1 = n1 * (1.0f / sqrtf(n1.x * n1.x + n1.y * n1.y)); + n2 = v2 - v3; + n2 = Vector2(-n2.y, n2.x); + n2 = n2 * (1.0f / sqrtf(n2.x * n2.x + n2.y * n2.y)); + n3 = v3 - v1; + n3 = Vector2(-n3.y, n3.x); + n3 = n3 * (1.0f / sqrtf(n3.x * n3.x + n3.y * n3.y)); + } + + // Vertices. + Vector2 v1, v2, v3; + Vector2 n1, n2, n3; // unit inward normals + Vector3 t1, t2, t3; + + // Deltas. + Vector3 dx, dy; + + float sign; + bool valid; +}; + +enum Mode { + Mode_Nearest, + Mode_Antialiased +}; + +// Process the given triangle. Returns false if rasterization was interrupted by the callback. +static bool drawTriangle(Mode mode, Vector2::Arg extents, bool enableScissors, const Vector2 v[3], SamplingCallback cb, void *param) { + Triangle tri(v[0], v[1], v[2], Vector3(1, 0, 0), Vector3(0, 1, 0), Vector3(0, 0, 1)); + // @@ It would be nice to have a conservative drawing mode that enlarges the triangle extents by one texel and is able to handle degenerate triangles. + // @@ Maybe the simplest thing to do would be raster triangle edges. + if (tri.valid) { + if (mode == Mode_Antialiased) { + return tri.drawAA(extents, enableScissors, cb, param); + } + if (mode == Mode_Nearest) { + return tri.draw(extents, enableScissors, cb, param); + } + } + return true; +} + +// Process the given quad. Returns false if rasterization was interrupted by the callback. +static bool drawQuad(Mode mode, Vector2::Arg extents, bool enableScissors, const Vector2 v[4], SamplingCallback cb, void *param) { + bool sign0 = triangleArea2(v[0], v[1], v[2]) > 0.0f; + bool sign1 = triangleArea2(v[0], v[2], v[3]) > 0.0f; + // Divide the quad into two non overlapping triangles. + if (sign0 == sign1) { + Triangle tri0(v[0], v[1], v[2], Vector3(0, 0, 0), Vector3(1, 0, 0), Vector3(1, 1, 0)); + Triangle tri1(v[0], v[2], v[3], Vector3(0, 0, 0), Vector3(1, 1, 0), Vector3(0, 1, 0)); + if (tri0.valid && tri1.valid) { + if (mode == Mode_Antialiased) { + return tri0.drawAA(extents, enableScissors, cb, param) && tri1.drawAA(extents, enableScissors, cb, param); + } else { + return tri0.draw(extents, enableScissors, cb, param) && tri1.draw(extents, enableScissors, cb, param); + } + } + } else { + Triangle tri0(v[0], v[1], v[3], Vector3(0, 0, 0), Vector3(1, 0, 0), Vector3(0, 1, 0)); + Triangle tri1(v[1], v[2], v[3], Vector3(1, 0, 0), Vector3(1, 1, 0), Vector3(0, 1, 0)); + if (tri0.valid && tri1.valid) { + if (mode == Mode_Antialiased) { + return tri0.drawAA(extents, enableScissors, cb, param) && tri1.drawAA(extents, enableScissors, cb, param); + } else { + return tri0.draw(extents, enableScissors, cb, param) && tri1.draw(extents, enableScissors, cb, param); + } + } + } + return true; +} +} // namespace raster + +// Full and sparse vector and matrix classes. BLAS subset. +// Pseudo-BLAS interface. +namespace sparse { +enum Transpose { + NoTransposed = 0, + Transposed = 1 +}; + +/** +* Sparse matrix class. The matrix is assumed to be sparse and to have +* very few non-zero elements, for this reason it's stored in indexed +* format. To multiply column vectors efficiently, the matrix stores +* the elements in indexed-column order, there is a list of indexed +* elements for each row of the matrix. As with the FullVector the +* dimension of the matrix is constant. +**/ +class Matrix { +public: + // An element of the sparse array. + struct Coefficient { + uint32_t x; // column + float v; // value + }; + + Matrix(uint32_t d) : + m_width(d) { m_array.resize(d); } + Matrix(uint32_t w, uint32_t h) : + m_width(w) { m_array.resize(h); } + Matrix(const Matrix &m) : + m_width(m.m_width) { m_array = m.m_array; } + + const Matrix &operator=(const Matrix &m) { + xaAssert(width() == m.width()); + xaAssert(height() == m.height()); + m_array = m.m_array; + return *this; + } + + uint32_t width() const { return m_width; } + uint32_t height() const { return m_array.size(); } + bool isSquare() const { return width() == height(); } + + // x is column, y is row + float getCoefficient(uint32_t x, uint32_t y) const { + xaDebugAssert(x < width()); + xaDebugAssert(y < height()); + const uint32_t count = m_array[y].size(); + for (uint32_t i = 0; i < count; i++) { + if (m_array[y][i].x == x) return m_array[y][i].v; + } + return 0.0f; + } + + void setCoefficient(uint32_t x, uint32_t y, float f) { + xaDebugAssert(x < width()); + xaDebugAssert(y < height()); + const uint32_t count = m_array[y].size(); + for (uint32_t i = 0; i < count; i++) { + if (m_array[y][i].x == x) { + m_array[y][i].v = f; + return; + } + } + if (f != 0.0f) { + Coefficient c = { x, f }; + m_array[y].push_back(c); + } + } + + float dotRow(uint32_t y, const FullVector &v) const { + xaDebugAssert(y < height()); + const uint32_t count = m_array[y].size(); + float sum = 0; + for (uint32_t i = 0; i < count; i++) { + sum += m_array[y][i].v * v[m_array[y][i].x]; + } + return sum; + } + + void madRow(uint32_t y, float alpha, FullVector &v) const { + xaDebugAssert(y < height()); + const uint32_t count = m_array[y].size(); + for (uint32_t i = 0; i < count; i++) { + v[m_array[y][i].x] += alpha * m_array[y][i].v; + } + } + + void clearRow(uint32_t y) { + xaDebugAssert(y < height()); + m_array[y].clear(); + } + + void scaleRow(uint32_t y, float f) { + xaDebugAssert(y < height()); + const uint32_t count = m_array[y].size(); + for (uint32_t i = 0; i < count; i++) { + m_array[y][i].v *= f; + } + } + + const std::vector<Coefficient> &getRow(uint32_t y) const { return m_array[y]; } + +private: + /// Number of columns. + const uint32_t m_width; + + /// Array of matrix elements. + std::vector<std::vector<Coefficient> > m_array; +}; + +// y = a * x + y +static void saxpy(float a, const FullVector &x, FullVector &y) { + xaDebugAssert(x.dimension() == y.dimension()); + const uint32_t dim = x.dimension(); + for (uint32_t i = 0; i < dim; i++) { + y[i] += a * x[i]; + } +} + +static void copy(const FullVector &x, FullVector &y) { + xaDebugAssert(x.dimension() == y.dimension()); + const uint32_t dim = x.dimension(); + for (uint32_t i = 0; i < dim; i++) { + y[i] = x[i]; + } +} + +static void scal(float a, FullVector &x) { + const uint32_t dim = x.dimension(); + for (uint32_t i = 0; i < dim; i++) { + x[i] *= a; + } +} + +static float dot(const FullVector &x, const FullVector &y) { + xaDebugAssert(x.dimension() == y.dimension()); + const uint32_t dim = x.dimension(); + float sum = 0; + for (uint32_t i = 0; i < dim; i++) { + sum += x[i] * y[i]; + } + return sum; +} + +static void mult(Transpose TM, const Matrix &M, const FullVector &x, FullVector &y) { + const uint32_t w = M.width(); + const uint32_t h = M.height(); + if (TM == Transposed) { + xaDebugAssert(h == x.dimension()); + xaDebugAssert(w == y.dimension()); + y.fill(0.0f); + for (uint32_t i = 0; i < h; i++) { + M.madRow(i, x[i], y); + } + } else { + xaDebugAssert(w == x.dimension()); + xaDebugAssert(h == y.dimension()); + for (uint32_t i = 0; i < h; i++) { + y[i] = M.dotRow(i, x); + } + } +} + +// y = M * x +static void mult(const Matrix &M, const FullVector &x, FullVector &y) { + mult(NoTransposed, M, x, y); +} + +static void sgemv(float alpha, Transpose TA, const Matrix &A, const FullVector &x, float beta, FullVector &y) { + const uint32_t w = A.width(); + const uint32_t h = A.height(); + if (TA == Transposed) { + xaDebugAssert(h == x.dimension()); + xaDebugAssert(w == y.dimension()); + for (uint32_t i = 0; i < h; i++) { + A.madRow(i, alpha * x[i], y); + } + } else { + xaDebugAssert(w == x.dimension()); + xaDebugAssert(h == y.dimension()); + for (uint32_t i = 0; i < h; i++) { + y[i] = alpha * A.dotRow(i, x) + beta * y[i]; + } + } +} + +// y = alpha*A*x + beta*y +static void sgemv(float alpha, const Matrix &A, const FullVector &x, float beta, FullVector &y) { + sgemv(alpha, NoTransposed, A, x, beta, y); +} + +// dot y-row of A by x-column of B +static float dotRowColumn(int y, const Matrix &A, int x, const Matrix &B) { + const std::vector<Matrix::Coefficient> &row = A.getRow(y); + const uint32_t count = row.size(); + float sum = 0.0f; + for (uint32_t i = 0; i < count; i++) { + const Matrix::Coefficient &c = row[i]; + sum += c.v * B.getCoefficient(x, c.x); + } + return sum; +} + +// dot y-row of A by x-row of B +static float dotRowRow(int y, const Matrix &A, int x, const Matrix &B) { + const std::vector<Matrix::Coefficient> &row = A.getRow(y); + const uint32_t count = row.size(); + float sum = 0.0f; + for (uint32_t i = 0; i < count; i++) { + const Matrix::Coefficient &c = row[i]; + sum += c.v * B.getCoefficient(c.x, x); + } + return sum; +} + +// dot y-column of A by x-column of B +static float dotColumnColumn(int y, const Matrix &A, int x, const Matrix &B) { + xaDebugAssert(A.height() == B.height()); + const uint32_t h = A.height(); + float sum = 0.0f; + for (uint32_t i = 0; i < h; i++) { + sum += A.getCoefficient(y, i) * B.getCoefficient(x, i); + } + return sum; +} + +static void transpose(const Matrix &A, Matrix &B) { + xaDebugAssert(A.width() == B.height()); + xaDebugAssert(B.width() == A.height()); + const uint32_t w = A.width(); + for (uint32_t x = 0; x < w; x++) { + B.clearRow(x); + } + const uint32_t h = A.height(); + for (uint32_t y = 0; y < h; y++) { + const std::vector<Matrix::Coefficient> &row = A.getRow(y); + const uint32_t count = row.size(); + for (uint32_t i = 0; i < count; i++) { + const Matrix::Coefficient &c = row[i]; + xaDebugAssert(c.x < w); + B.setCoefficient(y, c.x, c.v); + } + } +} + +static void sgemm(float alpha, Transpose TA, const Matrix &A, Transpose TB, const Matrix &B, float beta, Matrix &C) { + const uint32_t w = C.width(); + const uint32_t h = C.height(); + uint32_t aw = (TA == NoTransposed) ? A.width() : A.height(); + uint32_t ah = (TA == NoTransposed) ? A.height() : A.width(); + uint32_t bw = (TB == NoTransposed) ? B.width() : B.height(); + uint32_t bh = (TB == NoTransposed) ? B.height() : B.width(); + xaDebugAssert(aw == bh); + xaDebugAssert(bw == ah); + xaDebugAssert(w == bw); + xaDebugAssert(h == ah); +#ifdef NDEBUG + aw = ah = bw = bh = 0; // silence unused parameter warning +#endif + for (uint32_t y = 0; y < h; y++) { + for (uint32_t x = 0; x < w; x++) { + float c = beta * C.getCoefficient(x, y); + if (TA == NoTransposed && TB == NoTransposed) { + // dot y-row of A by x-column of B. + c += alpha * dotRowColumn(y, A, x, B); + } else if (TA == Transposed && TB == Transposed) { + // dot y-column of A by x-row of B. + c += alpha * dotRowColumn(x, B, y, A); + } else if (TA == Transposed && TB == NoTransposed) { + // dot y-column of A by x-column of B. + c += alpha * dotColumnColumn(y, A, x, B); + } else if (TA == NoTransposed && TB == Transposed) { + // dot y-row of A by x-row of B. + c += alpha * dotRowRow(y, A, x, B); + } + C.setCoefficient(x, y, c); + } + } +} + +static void mult(Transpose TA, const Matrix &A, Transpose TB, const Matrix &B, Matrix &C) { + sgemm(1.0f, TA, A, TB, B, 0.0f, C); +} + +// C = A * B +static void mult(const Matrix &A, const Matrix &B, Matrix &C) { + mult(NoTransposed, A, NoTransposed, B, C); +} + +} // namespace sparse + +class JacobiPreconditioner { +public: + JacobiPreconditioner(const sparse::Matrix &M, bool symmetric) : + m_inverseDiagonal(M.width()) { + xaAssert(M.isSquare()); + for (uint32_t x = 0; x < M.width(); x++) { + float elem = M.getCoefficient(x, x); + //xaDebugAssert( elem != 0.0f ); // This can be zero in the presence of zero area triangles. + if (symmetric) { + m_inverseDiagonal[x] = (elem != 0) ? 1.0f / sqrtf(fabsf(elem)) : 1.0f; + } else { + m_inverseDiagonal[x] = (elem != 0) ? 1.0f / elem : 1.0f; + } + } + } + + void apply(const FullVector &x, FullVector &y) const { + xaDebugAssert(x.dimension() == m_inverseDiagonal.dimension()); + xaDebugAssert(y.dimension() == m_inverseDiagonal.dimension()); + // @@ Wrap vector component-wise product into a separate function. + const uint32_t D = x.dimension(); + for (uint32_t i = 0; i < D; i++) { + y[i] = m_inverseDiagonal[i] * x[i]; + } + } + +private: + FullVector m_inverseDiagonal; +}; + +// Linear solvers. +class Solver { +public: + // Solve the symmetric system: At·A·x = At·b + static bool LeastSquaresSolver(const sparse::Matrix &A, const FullVector &b, FullVector &x, float epsilon = 1e-5f) { + xaDebugAssert(A.width() == x.dimension()); + xaDebugAssert(A.height() == b.dimension()); + xaDebugAssert(A.height() >= A.width()); // @@ If height == width we could solve it directly... + const uint32_t D = A.width(); + sparse::Matrix At(A.height(), A.width()); + sparse::transpose(A, At); + FullVector Atb(D); + sparse::mult(At, b, Atb); + sparse::Matrix AtA(D); + sparse::mult(At, A, AtA); + return SymmetricSolver(AtA, Atb, x, epsilon); + } + + // See section 10.4.3 in: Mesh Parameterization: Theory and Practice, Siggraph Course Notes, August 2007 + static bool LeastSquaresSolver(const sparse::Matrix &A, const FullVector &b, FullVector &x, const uint32_t *lockedParameters, uint32_t lockedCount, float epsilon = 1e-5f) { + xaDebugAssert(A.width() == x.dimension()); + xaDebugAssert(A.height() == b.dimension()); + xaDebugAssert(A.height() >= A.width() - lockedCount); + // @@ This is not the most efficient way of building a system with reduced degrees of freedom. It would be faster to do it on the fly. + const uint32_t D = A.width() - lockedCount; + xaDebugAssert(D > 0); + // Compute: b - Al * xl + FullVector b_Alxl(b); + for (uint32_t y = 0; y < A.height(); y++) { + const uint32_t count = A.getRow(y).size(); + for (uint32_t e = 0; e < count; e++) { + uint32_t column = A.getRow(y)[e].x; + bool isFree = true; + for (uint32_t i = 0; i < lockedCount; i++) { + isFree &= (lockedParameters[i] != column); + } + if (!isFree) { + b_Alxl[y] -= x[column] * A.getRow(y)[e].v; + } + } + } + // Remove locked columns from A. + sparse::Matrix Af(D, A.height()); + for (uint32_t y = 0; y < A.height(); y++) { + const uint32_t count = A.getRow(y).size(); + for (uint32_t e = 0; e < count; e++) { + uint32_t column = A.getRow(y)[e].x; + uint32_t ix = column; + bool isFree = true; + for (uint32_t i = 0; i < lockedCount; i++) { + isFree &= (lockedParameters[i] != column); + if (column > lockedParameters[i]) ix--; // shift columns + } + if (isFree) { + Af.setCoefficient(ix, y, A.getRow(y)[e].v); + } + } + } + // Remove elements from x + FullVector xf(D); + for (uint32_t i = 0, j = 0; i < A.width(); i++) { + bool isFree = true; + for (uint32_t l = 0; l < lockedCount; l++) { + isFree &= (lockedParameters[l] != i); + } + if (isFree) { + xf[j++] = x[i]; + } + } + // Solve reduced system. + bool result = LeastSquaresSolver(Af, b_Alxl, xf, epsilon); + // Copy results back to x. + for (uint32_t i = 0, j = 0; i < A.width(); i++) { + bool isFree = true; + for (uint32_t l = 0; l < lockedCount; l++) { + isFree &= (lockedParameters[l] != i); + } + if (isFree) { + x[i] = xf[j++]; + } + } + return result; + } + +private: + /** + * Compute the solution of the sparse linear system Ab=x using the Conjugate + * Gradient method. + * + * Solving sparse linear systems: + * (1) A·x = b + * + * The conjugate gradient algorithm solves (1) only in the case that A is + * symmetric and positive definite. It is based on the idea of minimizing the + * function + * + * (2) f(x) = 1/2·x·A·x - b·x + * + * This function is minimized when its gradient + * + * (3) df = A·x - b + * + * is zero, which is equivalent to (1). The minimization is carried out by + * generating a succession of search directions p.k and improved minimizers x.k. + * At each stage a quantity alfa.k is found that minimizes f(x.k + alfa.k·p.k), + * and x.k+1 is set equal to the new point x.k + alfa.k·p.k. The p.k and x.k are + * built up in such a way that x.k+1 is also the minimizer of f over the whole + * vector space of directions already taken, {p.1, p.2, . . . , p.k}. After N + * iterations you arrive at the minimizer over the entire vector space, i.e., the + * solution to (1). + * + * For a really good explanation of the method see: + * + * "An Introduction to the Conjugate Gradient Method Without the Agonizing Pain", + * Jonhathan Richard Shewchuk. + * + **/ + static bool ConjugateGradientSolver(const sparse::Matrix &A, const FullVector &b, FullVector &x, float epsilon) { + xaDebugAssert(A.isSquare()); + xaDebugAssert(A.width() == b.dimension()); + xaDebugAssert(A.width() == x.dimension()); + int i = 0; + const int D = A.width(); + const int i_max = 4 * D; // Convergence should be linear, but in some cases, it's not. + FullVector r(D); // residual + FullVector p(D); // search direction + FullVector q(D); // + float delta_0; + float delta_old; + float delta_new; + float alpha; + float beta; + // r = b - A·x; + sparse::copy(b, r); + sparse::sgemv(-1, A, x, 1, r); + // p = r; + sparse::copy(r, p); + delta_new = sparse::dot(r, r); + delta_0 = delta_new; + while (i < i_max && delta_new > epsilon * epsilon * delta_0) { + i++; + // q = A·p + mult(A, p, q); + // alpha = delta_new / p·q + alpha = delta_new / sparse::dot(p, q); + // x = alfa·p + x + sparse::saxpy(alpha, p, x); + if ((i & 31) == 0) { // recompute r after 32 steps + // r = b - A·x + sparse::copy(b, r); + sparse::sgemv(-1, A, x, 1, r); + } else { + // r = r - alpha·q + sparse::saxpy(-alpha, q, r); + } + delta_old = delta_new; + delta_new = sparse::dot(r, r); + beta = delta_new / delta_old; + // p = beta·p + r + sparse::scal(beta, p); + sparse::saxpy(1, r, p); + } + return delta_new <= epsilon * epsilon * delta_0; + } + + // Conjugate gradient with preconditioner. + static bool ConjugateGradientSolver(const JacobiPreconditioner &preconditioner, const sparse::Matrix &A, const FullVector &b, FullVector &x, float epsilon) { + xaDebugAssert(A.isSquare()); + xaDebugAssert(A.width() == b.dimension()); + xaDebugAssert(A.width() == x.dimension()); + int i = 0; + const int D = A.width(); + const int i_max = 4 * D; // Convergence should be linear, but in some cases, it's not. + FullVector r(D); // residual + FullVector p(D); // search direction + FullVector q(D); // + FullVector s(D); // preconditioned + float delta_0; + float delta_old; + float delta_new; + float alpha; + float beta; + // r = b - A·x + sparse::copy(b, r); + sparse::sgemv(-1, A, x, 1, r); + // p = M^-1 · r + preconditioner.apply(r, p); + delta_new = sparse::dot(r, p); + delta_0 = delta_new; + while (i < i_max && delta_new > epsilon * epsilon * delta_0) { + i++; + // q = A·p + mult(A, p, q); + // alpha = delta_new / p·q + alpha = delta_new / sparse::dot(p, q); + // x = alfa·p + x + sparse::saxpy(alpha, p, x); + if ((i & 31) == 0) { // recompute r after 32 steps + // r = b - A·x + sparse::copy(b, r); + sparse::sgemv(-1, A, x, 1, r); + } else { + // r = r - alfa·q + sparse::saxpy(-alpha, q, r); + } + // s = M^-1 · r + preconditioner.apply(r, s); + delta_old = delta_new; + delta_new = sparse::dot(r, s); + beta = delta_new / delta_old; + // p = s + beta·p + sparse::scal(beta, p); + sparse::saxpy(1, s, p); + } + return delta_new <= epsilon * epsilon * delta_0; + } + + static bool SymmetricSolver(const sparse::Matrix &A, const FullVector &b, FullVector &x, float epsilon = 1e-5f) { + xaDebugAssert(A.height() == A.width()); + xaDebugAssert(A.height() == b.dimension()); + xaDebugAssert(b.dimension() == x.dimension()); + JacobiPreconditioner jacobi(A, true); + return ConjugateGradientSolver(jacobi, A, b, x, epsilon); + } +}; + +namespace param { +class Atlas; +class Chart; + +// Fast sweep in 3 directions +static bool findApproximateDiameterVertices(halfedge::Mesh *mesh, halfedge::Vertex **a, halfedge::Vertex **b) { + xaDebugAssert(mesh != NULL); + xaDebugAssert(a != NULL); + xaDebugAssert(b != NULL); + const uint32_t vertexCount = mesh->vertexCount(); + halfedge::Vertex *minVertex[3]; + halfedge::Vertex *maxVertex[3]; + minVertex[0] = minVertex[1] = minVertex[2] = NULL; + maxVertex[0] = maxVertex[1] = maxVertex[2] = NULL; + for (uint32_t v = 1; v < vertexCount; v++) { + halfedge::Vertex *vertex = mesh->vertexAt(v); + xaDebugAssert(vertex != NULL); + if (vertex->isBoundary()) { + minVertex[0] = minVertex[1] = minVertex[2] = vertex; + maxVertex[0] = maxVertex[1] = maxVertex[2] = vertex; + break; + } + } + if (minVertex[0] == NULL) { + // Input mesh has not boundaries. + return false; + } + for (uint32_t v = 1; v < vertexCount; v++) { + halfedge::Vertex *vertex = mesh->vertexAt(v); + xaDebugAssert(vertex != NULL); + if (!vertex->isBoundary()) { + // Skip interior vertices. + continue; + } + if (vertex->pos.x < minVertex[0]->pos.x) + minVertex[0] = vertex; + else if (vertex->pos.x > maxVertex[0]->pos.x) + maxVertex[0] = vertex; + if (vertex->pos.y < minVertex[1]->pos.y) + minVertex[1] = vertex; + else if (vertex->pos.y > maxVertex[1]->pos.y) + maxVertex[1] = vertex; + if (vertex->pos.z < minVertex[2]->pos.z) + minVertex[2] = vertex; + else if (vertex->pos.z > maxVertex[2]->pos.z) + maxVertex[2] = vertex; + } + float lengths[3]; + for (int i = 0; i < 3; i++) { + lengths[i] = length(minVertex[i]->pos - maxVertex[i]->pos); + } + if (lengths[0] > lengths[1] && lengths[0] > lengths[2]) { + *a = minVertex[0]; + *b = maxVertex[0]; + } else if (lengths[1] > lengths[2]) { + *a = minVertex[1]; + *b = maxVertex[1]; + } else { + *a = minVertex[2]; + *b = maxVertex[2]; + } + return true; +} + +// Conformal relations from Brecht Van Lommel (based on ABF): + +static float vec_angle_cos(Vector3::Arg v1, Vector3::Arg v2, Vector3::Arg v3) { + Vector3 d1 = v1 - v2; + Vector3 d2 = v3 - v2; + return clamp(dot(d1, d2) / (length(d1) * length(d2)), -1.0f, 1.0f); +} + +static float vec_angle(Vector3::Arg v1, Vector3::Arg v2, Vector3::Arg v3) { + float dot = vec_angle_cos(v1, v2, v3); + return acosf(dot); +} + +static void triangle_angles(Vector3::Arg v1, Vector3::Arg v2, Vector3::Arg v3, float *a1, float *a2, float *a3) { + *a1 = vec_angle(v3, v1, v2); + *a2 = vec_angle(v1, v2, v3); + *a3 = PI - *a2 - *a1; +} + +static void setup_abf_relations(sparse::Matrix &A, int row, const halfedge::Vertex *v0, const halfedge::Vertex *v1, const halfedge::Vertex *v2) { + int id0 = v0->id; + int id1 = v1->id; + int id2 = v2->id; + Vector3 p0 = v0->pos; + Vector3 p1 = v1->pos; + Vector3 p2 = v2->pos; + // @@ IC: Wouldn't it be more accurate to return cos and compute 1-cos^2? + // It does indeed seem to be a little bit more robust. + // @@ Need to revisit this more carefully! + float a0, a1, a2; + triangle_angles(p0, p1, p2, &a0, &a1, &a2); + float s0 = sinf(a0); + float s1 = sinf(a1); + float s2 = sinf(a2); + if (s1 > s0 && s1 > s2) { + std::swap(s1, s2); + std::swap(s0, s1); + std::swap(a1, a2); + std::swap(a0, a1); + std::swap(id1, id2); + std::swap(id0, id1); + } else if (s0 > s1 && s0 > s2) { + std::swap(s0, s2); + std::swap(s0, s1); + std::swap(a0, a2); + std::swap(a0, a1); + std::swap(id0, id2); + std::swap(id0, id1); + } + float c0 = cosf(a0); + float ratio = (s2 == 0.0f) ? 1.0f : s1 / s2; + float cosine = c0 * ratio; + float sine = s0 * ratio; + // Note : 2*id + 0 --> u + // 2*id + 1 --> v + int u0_id = 2 * id0 + 0; + int v0_id = 2 * id0 + 1; + int u1_id = 2 * id1 + 0; + int v1_id = 2 * id1 + 1; + int u2_id = 2 * id2 + 0; + int v2_id = 2 * id2 + 1; + // Real part + A.setCoefficient(u0_id, 2 * row + 0, cosine - 1.0f); + A.setCoefficient(v0_id, 2 * row + 0, -sine); + A.setCoefficient(u1_id, 2 * row + 0, -cosine); + A.setCoefficient(v1_id, 2 * row + 0, sine); + A.setCoefficient(u2_id, 2 * row + 0, 1); + // Imaginary part + A.setCoefficient(u0_id, 2 * row + 1, sine); + A.setCoefficient(v0_id, 2 * row + 1, cosine - 1.0f); + A.setCoefficient(u1_id, 2 * row + 1, -sine); + A.setCoefficient(v1_id, 2 * row + 1, -cosine); + A.setCoefficient(v2_id, 2 * row + 1, 1); +} + +bool computeLeastSquaresConformalMap(halfedge::Mesh *mesh) { + xaDebugAssert(mesh != NULL); + // For this to work properly, mesh should not have colocals that have the same + // attributes, unless you want the vertices to actually have different texcoords. + const uint32_t vertexCount = mesh->vertexCount(); + const uint32_t D = 2 * vertexCount; + const uint32_t N = 2 * halfedge::countMeshTriangles(mesh); + // N is the number of equations (one per triangle) + // D is the number of variables (one per vertex; there are 2 pinned vertices). + if (N < D - 4) { + return false; + } + sparse::Matrix A(D, N); + FullVector b(N); + FullVector x(D); + // Fill b: + b.fill(0.0f); + // Fill x: + halfedge::Vertex *v0; + halfedge::Vertex *v1; + if (!findApproximateDiameterVertices(mesh, &v0, &v1)) { + // Mesh has no boundaries. + return false; + } + if (v0->tex == v1->tex) { + // LSCM expects an existing parameterization. + return false; + } + for (uint32_t v = 0; v < vertexCount; v++) { + halfedge::Vertex *vertex = mesh->vertexAt(v); + xaDebugAssert(vertex != NULL); + // Initial solution. + x[2 * v + 0] = vertex->tex.x; + x[2 * v + 1] = vertex->tex.y; + } + // Fill A: + const uint32_t faceCount = mesh->faceCount(); + for (uint32_t f = 0, t = 0; f < faceCount; f++) { + const halfedge::Face *face = mesh->faceAt(f); + xaDebugAssert(face != NULL); + xaDebugAssert(face->edgeCount() == 3); + const halfedge::Vertex *vertex0 = NULL; + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Edge *edge = it.current(); + xaAssert(edge != NULL); + if (vertex0 == NULL) { + vertex0 = edge->vertex; + } else if (edge->next->vertex != vertex0) { + const halfedge::Vertex *vertex1 = edge->from(); + const halfedge::Vertex *vertex2 = edge->to(); + setup_abf_relations(A, t, vertex0, vertex1, vertex2); + //setup_conformal_map_relations(A, t, vertex0, vertex1, vertex2); + t++; + } + } + } + const uint32_t lockedParameters[] = { + 2 * v0->id + 0, + 2 * v0->id + 1, + 2 * v1->id + 0, + 2 * v1->id + 1 + }; + // Solve + Solver::LeastSquaresSolver(A, b, x, lockedParameters, 4, 0.000001f); + // Map x back to texcoords: + for (uint32_t v = 0; v < vertexCount; v++) { + halfedge::Vertex *vertex = mesh->vertexAt(v); + xaDebugAssert(vertex != NULL); + vertex->tex = Vector2(x[2 * v + 0], x[2 * v + 1]); + } + return true; +} + +bool computeOrthogonalProjectionMap(halfedge::Mesh *mesh) { + Vector3 axis[2]; + uint32_t vertexCount = mesh->vertexCount(); + std::vector<Vector3> points(vertexCount); + points.resize(vertexCount); + for (uint32_t i = 0; i < vertexCount; i++) { + points[i] = mesh->vertexAt(i)->pos; + } + // Avoid redundant computations. + float matrix[6]; + Fit::computeCovariance(vertexCount, points.data(), matrix); + if (matrix[0] == 0 && matrix[3] == 0 && matrix[5] == 0) { + return false; + } + float eigenValues[3]; + Vector3 eigenVectors[3]; + if (!Fit::eigenSolveSymmetric3(matrix, eigenValues, eigenVectors)) { + return false; + } + axis[0] = normalize(eigenVectors[0]); + axis[1] = normalize(eigenVectors[1]); + // Project vertices to plane. + for (halfedge::Mesh::VertexIterator it(mesh->vertices()); !it.isDone(); it.advance()) { + halfedge::Vertex *vertex = it.current(); + vertex->tex.x = dot(axis[0], vertex->pos); + vertex->tex.y = dot(axis[1], vertex->pos); + } + return true; +} + +void computeSingleFaceMap(halfedge::Mesh *mesh) { + xaDebugAssert(mesh != NULL); + xaDebugAssert(mesh->faceCount() == 1); + halfedge::Face *face = mesh->faceAt(0); + xaAssert(face != NULL); + Vector3 p0 = face->edge->from()->pos; + Vector3 p1 = face->edge->to()->pos; + Vector3 X = normalizeSafe(p1 - p0, Vector3(0.0f), 0.0f); + Vector3 Z = face->normal(); + Vector3 Y = normalizeSafe(cross(Z, X), Vector3(0.0f), 0.0f); + uint32_t i = 0; + for (halfedge::Face::EdgeIterator it(face->edges()); !it.isDone(); it.advance(), i++) { + halfedge::Vertex *vertex = it.vertex(); + xaAssert(vertex != NULL); + if (i == 0) { + vertex->tex = Vector2(0); + } else { + Vector3 pn = vertex->pos; + float xn = dot((pn - p0), X); + float yn = dot((pn - p0), Y); + vertex->tex = Vector2(xn, yn); + } + } +} + +// Dummy implementation of a priority queue using sort at insertion. +// - Insertion is o(n) +// - Smallest element goes at the end, so that popping it is o(1). +// - Resorting is n*log(n) +// @@ Number of elements in the queue is usually small, and we'd have to rebalance often. I'm not sure it's worth implementing a heap. +// @@ Searcing at removal would remove the need for sorting when priorities change. +struct PriorityQueue { + PriorityQueue(uint32_t size = UINT_MAX) : + maxSize(size) {} + + void push(float priority, uint32_t face) { + uint32_t i = 0; + const uint32_t count = pairs.size(); + for (; i < count; i++) { + if (pairs[i].priority > priority) break; + } + Pair p = { priority, face }; + pairs.insert(pairs.begin() + i, p); + if (pairs.size() > maxSize) { + pairs.erase(pairs.begin()); + } + } + + // push face out of order, to be sorted later. + void push(uint32_t face) { + Pair p = { 0.0f, face }; + pairs.push_back(p); + } + + uint32_t pop() { + uint32_t f = pairs.back().face; + pairs.pop_back(); + return f; + } + + void sort() { + //sort(pairs); // @@ My intro sort appears to be much slower than it should! + std::sort(pairs.begin(), pairs.end()); + } + + void clear() { + pairs.clear(); + } + + uint32_t count() const { + return pairs.size(); + } + + float firstPriority() const { + return pairs.back().priority; + } + + const uint32_t maxSize; + + struct Pair { + bool operator<(const Pair &p) const { + return priority > p.priority; // !! Sort in inverse priority order! + } + + float priority; + uint32_t face; + }; + + std::vector<Pair> pairs; +}; + +struct ChartBuildData { + ChartBuildData(int p_id) : + id(p_id) { + planeNormal = Vector3(0); + centroid = Vector3(0); + coneAxis = Vector3(0); + coneAngle = 0; + area = 0; + boundaryLength = 0; + normalSum = Vector3(0); + centroidSum = Vector3(0); + } + + int id; + + // Proxy info: + Vector3 planeNormal; + Vector3 centroid; + Vector3 coneAxis; + float coneAngle; + + float area; + float boundaryLength; + Vector3 normalSum; + Vector3 centroidSum; + + std::vector<uint32_t> seeds; // @@ These could be a pointers to the halfedge faces directly. + std::vector<uint32_t> faces; + PriorityQueue candidates; +}; + +struct AtlasBuilder { + AtlasBuilder(const halfedge::Mesh *m) : + mesh(m), + facesLeft(m->faceCount()) { + const uint32_t faceCount = m->faceCount(); + faceChartArray.resize(faceCount, -1); + faceCandidateArray.resize(faceCount, (uint32_t)-1); + // @@ Floyd for the whole mesh is too slow. We could compute floyd progressively per patch as the patch grows. We need a better solution to compute most central faces. + //computeShortestPaths(); + // Precompute edge lengths and face areas. + uint32_t edgeCount = m->edgeCount(); + edgeLengths.resize(edgeCount); + for (uint32_t i = 0; i < edgeCount; i++) { + uint32_t id = m->edgeAt(i)->id; + xaDebugAssert(id / 2 == i); +#ifdef NDEBUG + id = 0; // silence unused parameter warning +#endif + edgeLengths[i] = m->edgeAt(i)->length(); + } + faceAreas.resize(faceCount); + for (uint32_t i = 0; i < faceCount; i++) { + faceAreas[i] = m->faceAt(i)->area(); + } + } + + ~AtlasBuilder() { + const uint32_t chartCount = chartArray.size(); + for (uint32_t i = 0; i < chartCount; i++) { + delete chartArray[i]; + } + } + + void markUnchartedFaces(const std::vector<uint32_t> &unchartedFaces) { + const uint32_t unchartedFaceCount = unchartedFaces.size(); + for (uint32_t i = 0; i < unchartedFaceCount; i++) { + uint32_t f = unchartedFaces[i]; + faceChartArray[f] = -2; + //faceCandidateArray[f] = -2; // @@ ? + removeCandidate(f); + } + xaDebugAssert(facesLeft >= unchartedFaceCount); + facesLeft -= unchartedFaceCount; + } + + void computeShortestPaths() { + const uint32_t faceCount = mesh->faceCount(); + shortestPaths.resize(faceCount * faceCount, FLT_MAX); + // Fill edges: + for (uint32_t i = 0; i < faceCount; i++) { + shortestPaths[i * faceCount + i] = 0.0f; + const halfedge::Face *face_i = mesh->faceAt(i); + Vector3 centroid_i = face_i->centroid(); + for (halfedge::Face::ConstEdgeIterator it(face_i->edges()); !it.isDone(); it.advance()) { + const halfedge::Edge *edge = it.current(); + if (!edge->isBoundary()) { + const halfedge::Face *face_j = edge->pair->face; + uint32_t j = face_j->id; + Vector3 centroid_j = face_j->centroid(); + shortestPaths[i * faceCount + j] = shortestPaths[j * faceCount + i] = length(centroid_i - centroid_j); + } + } + } + // Use Floyd-Warshall algorithm to compute all paths: + for (uint32_t k = 0; k < faceCount; k++) { + for (uint32_t i = 0; i < faceCount; i++) { + for (uint32_t j = 0; j < faceCount; j++) { + shortestPaths[i * faceCount + j] = std::min(shortestPaths[i * faceCount + j], shortestPaths[i * faceCount + k] + shortestPaths[k * faceCount + j]); + } + } + } + } + + void placeSeeds(float threshold, uint32_t maxSeedCount) { + // Instead of using a predefiened number of seeds: + // - Add seeds one by one, growing chart until a certain treshold. + // - Undo charts and restart growing process. + // @@ How can we give preference to faces far from sharp features as in the LSCM paper? + // - those points can be found using a simple flood filling algorithm. + // - how do we weight the probabilities? + for (uint32_t i = 0; i < maxSeedCount; i++) { + if (facesLeft == 0) { + // No faces left, stop creating seeds. + break; + } + createRandomChart(threshold); + } + } + + void createRandomChart(float threshold) { + ChartBuildData *chart = new ChartBuildData(chartArray.size()); + chartArray.push_back(chart); + // Pick random face that is not used by any chart yet. + uint32_t randomFaceIdx = rand.getRange(facesLeft - 1); + uint32_t i = 0; + for (uint32_t f = 0; f != randomFaceIdx; f++, i++) { + while (faceChartArray[i] != -1) + i++; + } + while (faceChartArray[i] != -1) + i++; + chart->seeds.push_back(i); + addFaceToChart(chart, i, true); + // Grow the chart as much as possible within the given threshold. + growChart(chart, threshold * 0.5f, facesLeft); + //growCharts(threshold - threshold * 0.75f / chartCount(), facesLeft); + } + + void addFaceToChart(ChartBuildData *chart, uint32_t f, bool recomputeProxy = false) { + // Add face to chart. + chart->faces.push_back(f); + xaDebugAssert(faceChartArray[f] == -1); + faceChartArray[f] = chart->id; + facesLeft--; + // Update area and boundary length. + chart->area = evaluateChartArea(chart, f); + chart->boundaryLength = evaluateBoundaryLength(chart, f); + chart->normalSum = evaluateChartNormalSum(chart, f); + chart->centroidSum = evaluateChartCentroidSum(chart, f); + if (recomputeProxy) { + // Update proxy and candidate's priorities. + updateProxy(chart); + } + // Update candidates. + removeCandidate(f); + updateCandidates(chart, f); + updatePriorities(chart); + } + + // Returns true if any of the charts can grow more. + bool growCharts(float threshold, uint32_t faceCount) { + // Using one global list. + faceCount = std::min(faceCount, facesLeft); + for (uint32_t i = 0; i < faceCount; i++) { + const Candidate &candidate = getBestCandidate(); + if (candidate.metric > threshold) { + return false; // Can't grow more. + } + addFaceToChart(candidate.chart, candidate.face); + } + return facesLeft != 0; // Can continue growing. + } + + bool growChart(ChartBuildData *chart, float threshold, uint32_t faceCount) { + // Try to add faceCount faces within threshold to chart. + for (uint32_t i = 0; i < faceCount;) { + if (chart->candidates.count() == 0 || chart->candidates.firstPriority() > threshold) { + return false; + } + uint32_t f = chart->candidates.pop(); + if (faceChartArray[f] == -1) { + addFaceToChart(chart, f); + i++; + } + } + if (chart->candidates.count() == 0 || chart->candidates.firstPriority() > threshold) { + return false; + } + return true; + } + + void resetCharts() { + const uint32_t faceCount = mesh->faceCount(); + for (uint32_t i = 0; i < faceCount; i++) { + faceChartArray[i] = -1; + faceCandidateArray[i] = (uint32_t)-1; + } + facesLeft = faceCount; + candidateArray.clear(); + const uint32_t chartCount = chartArray.size(); + for (uint32_t i = 0; i < chartCount; i++) { + ChartBuildData *chart = chartArray[i]; + const uint32_t seed = chart->seeds.back(); + chart->area = 0.0f; + chart->boundaryLength = 0.0f; + chart->normalSum = Vector3(0); + chart->centroidSum = Vector3(0); + chart->faces.clear(); + chart->candidates.clear(); + addFaceToChart(chart, seed); + } + } + + void updateCandidates(ChartBuildData *chart, uint32_t f) { + const halfedge::Face *face = mesh->faceAt(f); + // Traverse neighboring faces, add the ones that do not belong to any chart yet. + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Edge *edge = it.current()->pair; + if (!edge->isBoundary()) { + uint32_t faceId = edge->face->id; + if (faceChartArray[faceId] == -1) { + chart->candidates.push(faceId); + } + } + } + } + + void updateProxies() { + const uint32_t chartCount = chartArray.size(); + for (uint32_t i = 0; i < chartCount; i++) { + updateProxy(chartArray[i]); + } + } + + void updateProxy(ChartBuildData *chart) { + //#pragma message(NV_FILE_LINE "TODO: Use best fit plane instead of average normal.") + chart->planeNormal = normalizeSafe(chart->normalSum, Vector3(0), 0.0f); + chart->centroid = chart->centroidSum / float(chart->faces.size()); + } + + bool relocateSeeds() { + bool anySeedChanged = false; + const uint32_t chartCount = chartArray.size(); + for (uint32_t i = 0; i < chartCount; i++) { + if (relocateSeed(chartArray[i])) { + anySeedChanged = true; + } + } + return anySeedChanged; + } + + bool relocateSeed(ChartBuildData *chart) { + Vector3 centroid = computeChartCentroid(chart); + const uint32_t N = 10; // @@ Hardcoded to 10? + PriorityQueue bestTriangles(N); + // Find the first N triangles that fit the proxy best. + const uint32_t faceCount = chart->faces.size(); + for (uint32_t i = 0; i < faceCount; i++) { + float priority = evaluateProxyFitMetric(chart, chart->faces[i]); + bestTriangles.push(priority, chart->faces[i]); + } + // Of those, choose the most central triangle. + uint32_t mostCentral; + float maxDistance = -1; + const uint32_t bestCount = bestTriangles.count(); + for (uint32_t i = 0; i < bestCount; i++) { + const halfedge::Face *face = mesh->faceAt(bestTriangles.pairs[i].face); + Vector3 faceCentroid = face->triangleCenter(); + float distance = length(centroid - faceCentroid); + if (distance > maxDistance) { + maxDistance = distance; + mostCentral = bestTriangles.pairs[i].face; + } + } + xaDebugAssert(maxDistance >= 0); + // In order to prevent k-means cyles we record all the previously chosen seeds. + uint32_t index = std::find(chart->seeds.begin(), chart->seeds.end(), mostCentral) - chart->seeds.begin(); + if (index < chart->seeds.size()) { + // Move new seed to the end of the seed array. + uint32_t last = chart->seeds.size() - 1; + std::swap(chart->seeds[index], chart->seeds[last]); + return false; + } else { + // Append new seed. + chart->seeds.push_back(mostCentral); + return true; + } + } + + void updatePriorities(ChartBuildData *chart) { + // Re-evaluate candidate priorities. + uint32_t candidateCount = chart->candidates.count(); + for (uint32_t i = 0; i < candidateCount; i++) { + chart->candidates.pairs[i].priority = evaluatePriority(chart, chart->candidates.pairs[i].face); + if (faceChartArray[chart->candidates.pairs[i].face] == -1) { + updateCandidate(chart, chart->candidates.pairs[i].face, chart->candidates.pairs[i].priority); + } + } + // Sort candidates. + chart->candidates.sort(); + } + + // Evaluate combined metric. + float evaluatePriority(ChartBuildData *chart, uint32_t face) { + // Estimate boundary length and area: + float newBoundaryLength = evaluateBoundaryLength(chart, face); + float newChartArea = evaluateChartArea(chart, face); + float F = evaluateProxyFitMetric(chart, face); + float C = evaluateRoundnessMetric(chart, face, newBoundaryLength, newChartArea); + float P = evaluateStraightnessMetric(chart, face); + // Penalize faces that cross seams, reward faces that close seams or reach boundaries. + float N = evaluateNormalSeamMetric(chart, face); + float T = evaluateTextureSeamMetric(chart, face); + //float R = evaluateCompletenessMetric(chart, face); + //float D = evaluateDihedralAngleMetric(chart, face); + // @@ Add a metric based on local dihedral angle. + // @@ Tweaking the normal and texture seam metrics. + // - Cause more impedance. Never cross 90 degree edges. + // - + float cost = float( + options.proxyFitMetricWeight * F + + options.roundnessMetricWeight * C + + options.straightnessMetricWeight * P + + options.normalSeamMetricWeight * N + + options.textureSeamMetricWeight * T); + // Enforce limits strictly: + if (newChartArea > options.maxChartArea) cost = FLT_MAX; + if (newBoundaryLength > options.maxBoundaryLength) cost = FLT_MAX; + // Make sure normal seams are fully respected: + if (options.normalSeamMetricWeight >= 1000 && N != 0) cost = FLT_MAX; + xaAssert(std::isfinite(cost)); + return cost; + } + + // Returns a value in [0-1]. + float evaluateProxyFitMetric(ChartBuildData *chart, uint32_t f) { + const halfedge::Face *face = mesh->faceAt(f); + Vector3 faceNormal = face->triangleNormal(); + // Use plane fitting metric for now: + return 1 - dot(faceNormal, chart->planeNormal); // @@ normal deviations should be weighted by face area + } + + float evaluateRoundnessMetric(ChartBuildData *chart, uint32_t /*face*/, float newBoundaryLength, float newChartArea) { + float roundness = square(chart->boundaryLength) / chart->area; + float newRoundness = square(newBoundaryLength) / newChartArea; + if (newRoundness > roundness) { + return square(newBoundaryLength) / (newChartArea * 4 * PI); + } else { + // Offer no impedance to faces that improve roundness. + return 0; + } + } + + float evaluateStraightnessMetric(ChartBuildData *chart, uint32_t f) { + float l_out = 0.0f; + float l_in = 0.0f; + const halfedge::Face *face = mesh->faceAt(f); + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Edge *edge = it.current(); + float l = edgeLengths[edge->id / 2]; + if (edge->isBoundary()) { + l_out += l; + } else { + uint32_t neighborFaceId = edge->pair->face->id; + if (faceChartArray[neighborFaceId] != chart->id) { + l_out += l; + } else { + l_in += l; + } + } + } + xaDebugAssert(l_in != 0.0f); // Candidate face must be adjacent to chart. @@ This is not true if the input mesh has zero-length edges. + float ratio = (l_out - l_in) / (l_out + l_in); + return std::min(ratio, 0.0f); // Only use the straightness metric to close gaps. + } + + float evaluateNormalSeamMetric(ChartBuildData *chart, uint32_t f) { + float seamFactor = 0.0f; + float totalLength = 0.0f; + const halfedge::Face *face = mesh->faceAt(f); + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Edge *edge = it.current(); + if (edge->isBoundary()) { + continue; + } + const uint32_t neighborFaceId = edge->pair->face->id; + if (faceChartArray[neighborFaceId] != chart->id) { + continue; + } + //float l = edge->length(); + float l = edgeLengths[edge->id / 2]; + totalLength += l; + if (!edge->isSeam()) { + continue; + } + // Make sure it's a normal seam. + if (edge->isNormalSeam()) { + float d0 = clamp(dot(edge->vertex->nor, edge->pair->next->vertex->nor), 0.0f, 1.0f); + float d1 = clamp(dot(edge->next->vertex->nor, edge->pair->vertex->nor), 0.0f, 1.0f); + l *= 1 - (d0 + d1) * 0.5f; + seamFactor += l; + } + } + if (seamFactor == 0) return 0.0f; + return seamFactor / totalLength; + } + + float evaluateTextureSeamMetric(ChartBuildData *chart, uint32_t f) { + float seamLength = 0.0f; + float totalLength = 0.0f; + const halfedge::Face *face = mesh->faceAt(f); + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Edge *edge = it.current(); + if (edge->isBoundary()) { + continue; + } + const uint32_t neighborFaceId = edge->pair->face->id; + if (faceChartArray[neighborFaceId] != chart->id) { + continue; + } + //float l = edge->length(); + float l = edgeLengths[edge->id / 2]; + totalLength += l; + if (!edge->isSeam()) { + continue; + } + // Make sure it's a texture seam. + if (edge->isTextureSeam()) { + seamLength += l; + } + } + if (seamLength == 0.0f) { + return 0.0f; // Avoid division by zero. + } + return seamLength / totalLength; + } + + float evaluateChartArea(ChartBuildData *chart, uint32_t f) { + const halfedge::Face *face = mesh->faceAt(f); + return chart->area + faceAreas[face->id]; + } + + float evaluateBoundaryLength(ChartBuildData *chart, uint32_t f) { + float boundaryLength = chart->boundaryLength; + // Add new edges, subtract edges shared with the chart. + const halfedge::Face *face = mesh->faceAt(f); + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Edge *edge = it.current(); + //float edgeLength = edge->length(); + float edgeLength = edgeLengths[edge->id / 2]; + if (edge->isBoundary()) { + boundaryLength += edgeLength; + } else { + uint32_t neighborFaceId = edge->pair->face->id; + if (faceChartArray[neighborFaceId] != chart->id) { + boundaryLength += edgeLength; + } else { + boundaryLength -= edgeLength; + } + } + } + return std::max(0.0f, boundaryLength); // @@ Hack! + } + + Vector3 evaluateChartNormalSum(ChartBuildData *chart, uint32_t f) { + const halfedge::Face *face = mesh->faceAt(f); + return chart->normalSum + face->triangleNormalAreaScaled(); + } + + Vector3 evaluateChartCentroidSum(ChartBuildData *chart, uint32_t f) { + const halfedge::Face *face = mesh->faceAt(f); + return chart->centroidSum + face->centroid(); + } + + Vector3 computeChartCentroid(const ChartBuildData *chart) { + Vector3 centroid(0); + const uint32_t faceCount = chart->faces.size(); + for (uint32_t i = 0; i < faceCount; i++) { + const halfedge::Face *face = mesh->faceAt(chart->faces[i]); + centroid += face->triangleCenter(); + } + return centroid / float(faceCount); + } + + void fillHoles(float threshold) { + while (facesLeft > 0) + createRandomChart(threshold); + } + + void mergeCharts() { + std::vector<float> sharedBoundaryLengths; + const uint32_t chartCount = chartArray.size(); + for (int c = chartCount - 1; c >= 0; c--) { + sharedBoundaryLengths.clear(); + sharedBoundaryLengths.resize(chartCount, 0.0f); + ChartBuildData *chart = chartArray[c]; + float externalBoundary = 0.0f; + const uint32_t faceCount = chart->faces.size(); + for (uint32_t i = 0; i < faceCount; i++) { + uint32_t f = chart->faces[i]; + const halfedge::Face *face = mesh->faceAt(f); + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Edge *edge = it.current(); + //float l = edge->length(); + float l = edgeLengths[edge->id / 2]; + if (edge->isBoundary()) { + externalBoundary += l; + } else { + uint32_t neighborFace = edge->pair->face->id; + uint32_t neighborChart = faceChartArray[neighborFace]; + if (neighborChart != (uint32_t)c) { + if ((edge->isSeam() && (edge->isNormalSeam() || edge->isTextureSeam())) || neighborChart == -2) { + externalBoundary += l; + } else { + sharedBoundaryLengths[neighborChart] += l; + } + } + } + } + } + for (int cc = chartCount - 1; cc >= 0; cc--) { + if (cc == c) + continue; + ChartBuildData *chart2 = chartArray[cc]; + if (chart2 == NULL) + continue; + if (sharedBoundaryLengths[cc] > 0.8 * std::max(0.0f, chart->boundaryLength - externalBoundary)) { + // Try to avoid degenerate configurations. + if (chart2->boundaryLength > sharedBoundaryLengths[cc]) { + if (dot(chart2->planeNormal, chart->planeNormal) > -0.25) { + mergeChart(chart2, chart, sharedBoundaryLengths[cc]); + delete chart; + chartArray[c] = NULL; + break; + } + } + } + if (sharedBoundaryLengths[cc] > 0.20 * std::max(0.0f, chart->boundaryLength - externalBoundary)) { + // Compare proxies. + if (dot(chart2->planeNormal, chart->planeNormal) > 0) { + mergeChart(chart2, chart, sharedBoundaryLengths[cc]); + delete chart; + chartArray[c] = NULL; + break; + } + } + } + } + // Remove deleted charts. + for (int c = 0; c < int32_t(chartArray.size()); /*do not increment if removed*/) { + if (chartArray[c] == NULL) { + chartArray.erase(chartArray.begin() + c); + // Update faceChartArray. + const uint32_t faceCount = faceChartArray.size(); + for (uint32_t i = 0; i < faceCount; i++) { + xaDebugAssert(faceChartArray[i] != -1); + xaDebugAssert(faceChartArray[i] != c); + xaDebugAssert(faceChartArray[i] <= int32_t(chartArray.size())); + if (faceChartArray[i] > c) { + faceChartArray[i]--; + } + } + } else { + chartArray[c]->id = c; + c++; + } + } + } + + // @@ Cleanup. + struct Candidate { + uint32_t face; + ChartBuildData *chart; + float metric; + }; + + // @@ Get N best candidates in one pass. + const Candidate &getBestCandidate() const { + uint32_t best = 0; + float bestCandidateMetric = FLT_MAX; + const uint32_t candidateCount = candidateArray.size(); + xaAssert(candidateCount > 0); + for (uint32_t i = 0; i < candidateCount; i++) { + const Candidate &candidate = candidateArray[i]; + if (candidate.metric < bestCandidateMetric) { + bestCandidateMetric = candidate.metric; + best = i; + } + } + return candidateArray[best]; + } + + void removeCandidate(uint32_t f) { + int c = faceCandidateArray[f]; + if (c != -1) { + faceCandidateArray[f] = (uint32_t)-1; + if (c == int(candidateArray.size() - 1)) { + candidateArray.pop_back(); + } else { + // Replace with last. + candidateArray[c] = candidateArray[candidateArray.size() - 1]; + candidateArray.pop_back(); + faceCandidateArray[candidateArray[c].face] = c; + } + } + } + + void updateCandidate(ChartBuildData *chart, uint32_t f, float metric) { + if (faceCandidateArray[f] == -1) { + const uint32_t index = candidateArray.size(); + faceCandidateArray[f] = index; + candidateArray.resize(index + 1); + candidateArray[index].face = f; + candidateArray[index].chart = chart; + candidateArray[index].metric = metric; + } else { + int c = faceCandidateArray[f]; + xaDebugAssert(c != -1); + Candidate &candidate = candidateArray[c]; + xaDebugAssert(candidate.face == f); + if (metric < candidate.metric || chart == candidate.chart) { + candidate.metric = metric; + candidate.chart = chart; + } + } + } + + void mergeChart(ChartBuildData *owner, ChartBuildData *chart, float sharedBoundaryLength) { + const uint32_t faceCount = chart->faces.size(); + for (uint32_t i = 0; i < faceCount; i++) { + uint32_t f = chart->faces[i]; + xaDebugAssert(faceChartArray[f] == chart->id); + faceChartArray[f] = owner->id; + owner->faces.push_back(f); + } + // Update adjacencies? + owner->area += chart->area; + owner->boundaryLength += chart->boundaryLength - sharedBoundaryLength; + owner->normalSum += chart->normalSum; + owner->centroidSum += chart->centroidSum; + updateProxy(owner); + } + + uint32_t chartCount() const { return chartArray.size(); } + const std::vector<uint32_t> &chartFaces(uint32_t i) const { return chartArray[i]->faces; } + + const halfedge::Mesh *mesh; + uint32_t facesLeft; + std::vector<int> faceChartArray; + std::vector<ChartBuildData *> chartArray; + std::vector<float> shortestPaths; + std::vector<float> edgeLengths; + std::vector<float> faceAreas; + std::vector<Candidate> candidateArray; // + std::vector<uint32_t> faceCandidateArray; // Map face index to candidate index. + MTRand rand; + CharterOptions options; +}; + +/// A chart is a connected set of faces with a certain topology (usually a disk). +class Chart { +public: + Chart() : + m_isDisk(false), + m_isVertexMapped(false) {} + + void build(const halfedge::Mesh *originalMesh, const std::vector<uint32_t> &faceArray) { + // Copy face indices. + m_faceArray = faceArray; + const uint32_t meshVertexCount = originalMesh->vertexCount(); + m_chartMesh.reset(new halfedge::Mesh()); + m_unifiedMesh.reset(new halfedge::Mesh()); + std::vector<uint32_t> chartMeshIndices(meshVertexCount, (uint32_t)~0); + std::vector<uint32_t> unifiedMeshIndices(meshVertexCount, (uint32_t)~0); + // Add vertices. + const uint32_t faceCount = faceArray.size(); + for (uint32_t f = 0; f < faceCount; f++) { + const halfedge::Face *face = originalMesh->faceAt(faceArray[f]); + xaDebugAssert(face != NULL); + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Vertex *vertex = it.current()->vertex; + const halfedge::Vertex *unifiedVertex = vertex->firstColocal(); + if (unifiedMeshIndices[unifiedVertex->id] == ~0) { + unifiedMeshIndices[unifiedVertex->id] = m_unifiedMesh->vertexCount(); + xaDebugAssert(vertex->pos == unifiedVertex->pos); + m_unifiedMesh->addVertex(vertex->pos); + } + if (chartMeshIndices[vertex->id] == ~0) { + chartMeshIndices[vertex->id] = m_chartMesh->vertexCount(); + m_chartToOriginalMap.push_back(vertex->original_id); + m_chartToUnifiedMap.push_back(unifiedMeshIndices[unifiedVertex->id]); + halfedge::Vertex *v = m_chartMesh->addVertex(vertex->pos); + v->nor = vertex->nor; + v->tex = vertex->tex; + } + } + } + // This is ignoring the canonical map: + // - Is it really necessary to link colocals? + m_chartMesh->linkColocals(); + //m_unifiedMesh->linkColocals(); // Not strictly necessary, no colocals in the unified mesh. # Wrong. + // This check is not valid anymore, if the original mesh vertices were linked with a canonical map, then it might have + // some colocal vertices that were unlinked. So, the unified mesh might have some duplicate vertices, because firstColocal() + // is not guaranteed to return the same vertex for two colocal vertices. + //xaAssert(m_chartMesh->colocalVertexCount() == m_unifiedMesh->vertexCount()); + // Is that OK? What happens in meshes were that happens? Does anything break? Apparently not... + std::vector<uint32_t> faceIndices; + faceIndices.reserve(7); + // Add faces. + for (uint32_t f = 0; f < faceCount; f++) { + const halfedge::Face *face = originalMesh->faceAt(faceArray[f]); + xaDebugAssert(face != NULL); + faceIndices.clear(); + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Vertex *vertex = it.current()->vertex; + xaDebugAssert(vertex != NULL); + faceIndices.push_back(chartMeshIndices[vertex->id]); + } + m_chartMesh->addFace(faceIndices); + faceIndices.clear(); + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Vertex *vertex = it.current()->vertex; + xaDebugAssert(vertex != NULL); + vertex = vertex->firstColocal(); + faceIndices.push_back(unifiedMeshIndices[vertex->id]); + } + m_unifiedMesh->addFace(faceIndices); + } + m_chartMesh->linkBoundary(); + m_unifiedMesh->linkBoundary(); + //exportMesh(m_unifiedMesh.ptr(), "debug_input.obj"); + if (m_unifiedMesh->splitBoundaryEdges()) { + m_unifiedMesh.reset(halfedge::unifyVertices(m_unifiedMesh.get())); + } + //exportMesh(m_unifiedMesh.ptr(), "debug_split.obj"); + // Closing the holes is not always the best solution and does not fix all the problems. + // We need to do some analysis of the holes and the genus to: + // - Find cuts that reduce genus. + // - Find cuts to connect holes. + // - Use minimal spanning trees or seamster. + if (!closeHoles()) { + /*static int pieceCount = 0; + StringBuilder fileName; + fileName.format("debug_hole_%d.obj", pieceCount++); + exportMesh(m_unifiedMesh.ptr(), fileName.str());*/ + } + m_unifiedMesh.reset(halfedge::triangulate(m_unifiedMesh.get())); + //exportMesh(m_unifiedMesh.ptr(), "debug_triangulated.obj"); + // Analyze chart topology. + halfedge::MeshTopology topology(m_unifiedMesh.get()); + m_isDisk = topology.isDisk(); + } + + void buildVertexMap(const halfedge::Mesh *originalMesh, const std::vector<uint32_t> &unchartedMaterialArray) { + xaAssert(m_chartMesh.get() == NULL && m_unifiedMesh.get() == NULL); + m_isVertexMapped = true; + // Build face indices. + m_faceArray.clear(); + const uint32_t meshFaceCount = originalMesh->faceCount(); + for (uint32_t f = 0; f < meshFaceCount; f++) { + const halfedge::Face *face = originalMesh->faceAt(f); + if (std::find(unchartedMaterialArray.begin(), unchartedMaterialArray.end(), face->material) != unchartedMaterialArray.end()) { + m_faceArray.push_back(f); + } + } + const uint32_t faceCount = m_faceArray.size(); + if (faceCount == 0) { + return; + } + // @@ The chartMesh construction is basically the same as with regular charts, don't duplicate! + const uint32_t meshVertexCount = originalMesh->vertexCount(); + m_chartMesh.reset(new halfedge::Mesh()); + std::vector<uint32_t> chartMeshIndices(meshVertexCount, (uint32_t)~0); + // Vertex map mesh only has disconnected vertices. + for (uint32_t f = 0; f < faceCount; f++) { + const halfedge::Face *face = originalMesh->faceAt(m_faceArray[f]); + xaDebugAssert(face != NULL); + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Vertex *vertex = it.current()->vertex; + if (chartMeshIndices[vertex->id] == ~0) { + chartMeshIndices[vertex->id] = m_chartMesh->vertexCount(); + m_chartToOriginalMap.push_back(vertex->original_id); + halfedge::Vertex *v = m_chartMesh->addVertex(vertex->pos); + v->nor = vertex->nor; + v->tex = vertex->tex; // @@ Not necessary. + } + } + } + // @@ Link colocals using the original mesh canonical map? Build canonical map on the fly? Do we need to link colocals at all for this? + //m_chartMesh->linkColocals(); + std::vector<uint32_t> faceIndices; + faceIndices.reserve(7); + // Add faces. + for (uint32_t f = 0; f < faceCount; f++) { + const halfedge::Face *face = originalMesh->faceAt(m_faceArray[f]); + xaDebugAssert(face != NULL); + faceIndices.clear(); + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Vertex *vertex = it.current()->vertex; + xaDebugAssert(vertex != NULL); + xaDebugAssert(chartMeshIndices[vertex->id] != ~0); + faceIndices.push_back(chartMeshIndices[vertex->id]); + } + halfedge::Face *new_face = m_chartMesh->addFace(faceIndices); + xaDebugAssert(new_face != NULL); +#ifdef NDEBUG + new_face = NULL; // silence unused parameter warning +#endif + } + m_chartMesh->linkBoundary(); + const uint32_t chartVertexCount = m_chartMesh->vertexCount(); + Box bounds; + bounds.clearBounds(); + for (uint32_t i = 0; i < chartVertexCount; i++) { + halfedge::Vertex *vertex = m_chartMesh->vertexAt(i); + bounds.addPointToBounds(vertex->pos); + } + ProximityGrid grid; + grid.init(bounds, chartVertexCount); + for (uint32_t i = 0; i < chartVertexCount; i++) { + halfedge::Vertex *vertex = m_chartMesh->vertexAt(i); + grid.add(vertex->pos, i); + } + uint32_t texelCount = 0; + const float positionThreshold = 0.01f; + const float normalThreshold = 0.01f; + uint32_t verticesVisited = 0; + uint32_t cellsVisited = 0; + std::vector<int> vertexIndexArray(chartVertexCount, -1); // Init all indices to -1. + // Traverse vertices in morton order. @@ It may be more interesting to sort them based on orientation. + const uint32_t cellCodeCount = grid.mortonCount(); + for (uint32_t cellCode = 0; cellCode < cellCodeCount; cellCode++) { + int cell = grid.mortonIndex(cellCode); + if (cell < 0) continue; + cellsVisited++; + const std::vector<uint32_t> &indexArray = grid.cellArray[cell].indexArray; + for (uint32_t i = 0; i < indexArray.size(); i++) { + uint32_t idx = indexArray[i]; + halfedge::Vertex *vertex = m_chartMesh->vertexAt(idx); + xaDebugAssert(vertexIndexArray[idx] == -1); + std::vector<uint32_t> neighbors; + grid.gather(vertex->pos, positionThreshold, /*ref*/ neighbors); + // Compare against all nearby vertices, cluster greedily. + for (uint32_t j = 0; j < neighbors.size(); j++) { + uint32_t otherIdx = neighbors[j]; + if (vertexIndexArray[otherIdx] != -1) { + halfedge::Vertex *otherVertex = m_chartMesh->vertexAt(otherIdx); + if (distance(vertex->pos, otherVertex->pos) < positionThreshold && + distance(vertex->nor, otherVertex->nor) < normalThreshold) { + vertexIndexArray[idx] = vertexIndexArray[otherIdx]; + break; + } + } + } + // If index not assigned, assign new one. + if (vertexIndexArray[idx] == -1) { + vertexIndexArray[idx] = texelCount++; + } + verticesVisited++; + } + } + xaDebugAssert(cellsVisited == grid.cellArray.size()); + xaDebugAssert(verticesVisited == chartVertexCount); + vertexMapWidth = ftoi_ceil(sqrtf(float(texelCount))); + vertexMapWidth = (vertexMapWidth + 3) & ~3; // Width aligned to 4. + vertexMapHeight = vertexMapWidth == 0 ? 0 : (texelCount + vertexMapWidth - 1) / vertexMapWidth; + //vertexMapHeight = (vertexMapHeight + 3) & ~3; // Height aligned to 4. + xaDebugAssert(vertexMapWidth >= vertexMapHeight); + xaPrint("Reduced vertex count from %d to %d.\n", chartVertexCount, texelCount); + // Lay down the clustered vertices in morton order. + std::vector<uint32_t> texelCodes(texelCount); + // For each texel, assign one morton code. + uint32_t texelCode = 0; + for (uint32_t i = 0; i < texelCount; i++) { + uint32_t x, y; + do { + x = morton::decodeMorton2X(texelCode); + y = morton::decodeMorton2Y(texelCode); + texelCode++; + } while (x >= uint32_t(vertexMapWidth) || y >= uint32_t(vertexMapHeight)); + texelCodes[i] = texelCode - 1; + } + for (uint32_t i = 0; i < chartVertexCount; i++) { + halfedge::Vertex *vertex = m_chartMesh->vertexAt(i); + int idx = vertexIndexArray[i]; + if (idx != -1) { + uint32_t tc = texelCodes[idx]; + uint32_t x = morton::decodeMorton2X(tc); + uint32_t y = morton::decodeMorton2Y(tc); + vertex->tex.x = float(x); + vertex->tex.y = float(y); + } + } + } + + bool closeHoles() { + xaDebugAssert(!m_isVertexMapped); + std::vector<halfedge::Edge *> boundaryEdges; + getBoundaryEdges(m_unifiedMesh.get(), boundaryEdges); + uint32_t boundaryCount = boundaryEdges.size(); + if (boundaryCount <= 1) { + // Nothing to close. + return true; + } + // Compute lengths and areas. + std::vector<float> boundaryLengths; + for (uint32_t i = 0; i < boundaryCount; i++) { + const halfedge::Edge *startEdge = boundaryEdges[i]; + xaAssert(startEdge->face == NULL); + //float boundaryEdgeCount = 0; + float boundaryLength = 0.0f; + //Vector3 boundaryCentroid(zero); + const halfedge::Edge *edge = startEdge; + do { + Vector3 t0 = edge->from()->pos; + Vector3 t1 = edge->to()->pos; + //boundaryEdgeCount++; + boundaryLength += length(t1 - t0); + //boundaryCentroid += edge->vertex()->pos; + edge = edge->next; + } while (edge != startEdge); + boundaryLengths.push_back(boundaryLength); + //boundaryCentroids.append(boundaryCentroid / boundaryEdgeCount); + } + // Find disk boundary. + uint32_t diskBoundary = 0; + float maxLength = boundaryLengths[0]; + for (uint32_t i = 1; i < boundaryCount; i++) { + if (boundaryLengths[i] > maxLength) { + maxLength = boundaryLengths[i]; + diskBoundary = i; + } + } + // Close holes. + for (uint32_t i = 0; i < boundaryCount; i++) { + if (diskBoundary == i) { + // Skip disk boundary. + continue; + } + halfedge::Edge *startEdge = boundaryEdges[i]; + xaDebugAssert(startEdge != NULL); + xaDebugAssert(startEdge->face == NULL); + std::vector<halfedge::Vertex *> vertexLoop; + std::vector<halfedge::Edge *> edgeLoop; + halfedge::Edge *edge = startEdge; + do { + halfedge::Vertex *vertex = edge->next->vertex; // edge->to() + uint32_t j; + for (j = 0; j < vertexLoop.size(); j++) { + if (vertex->isColocal(vertexLoop[j])) { + break; + } + } + bool isCrossing = (j != vertexLoop.size()); + if (isCrossing) { + halfedge::Edge *prev = edgeLoop[j]; // Previous edge before the loop. + halfedge::Edge *next = edge->next; // Next edge after the loop. + xaDebugAssert(prev->to()->isColocal(next->from())); + // Close loop. + edgeLoop.push_back(edge); + closeLoop(j + 1, edgeLoop); + // Link boundary loop. + prev->setNext(next); + vertex->setEdge(next); + // Start over again. + vertexLoop.clear(); + edgeLoop.clear(); + edge = startEdge; + vertex = edge->to(); + } + vertexLoop.push_back(vertex); + edgeLoop.push_back(edge); + edge = edge->next; + } while (edge != startEdge); + closeLoop(0, edgeLoop); + } + getBoundaryEdges(m_unifiedMesh.get(), boundaryEdges); + boundaryCount = boundaryEdges.size(); + xaDebugAssert(boundaryCount == 1); + return boundaryCount == 1; + } + + bool isDisk() const { + return m_isDisk; + } + bool isVertexMapped() const { + return m_isVertexMapped; + } + + uint32_t vertexCount() const { + return m_chartMesh->vertexCount(); + } + uint32_t colocalVertexCount() const { + return m_unifiedMesh->vertexCount(); + } + + uint32_t faceCount() const { + return m_faceArray.size(); + } + uint32_t faceAt(uint32_t i) const { + return m_faceArray[i]; + } + + const halfedge::Mesh *chartMesh() const { + return m_chartMesh.get(); + } + halfedge::Mesh *chartMesh() { + return m_chartMesh.get(); + } + const halfedge::Mesh *unifiedMesh() const { + return m_unifiedMesh.get(); + } + halfedge::Mesh *unifiedMesh() { + return m_unifiedMesh.get(); + } + + //uint32_t vertexIndex(uint32_t i) const { return m_vertexIndexArray[i]; } + + uint32_t mapChartVertexToOriginalVertex(uint32_t i) const { + return m_chartToOriginalMap[i]; + } + uint32_t mapChartVertexToUnifiedVertex(uint32_t i) const { + return m_chartToUnifiedMap[i]; + } + + const std::vector<uint32_t> &faceArray() const { + return m_faceArray; + } + + // Transfer parameterization from unified mesh to chart mesh. + void transferParameterization() { + xaDebugAssert(!m_isVertexMapped); + uint32_t vertexCount = m_chartMesh->vertexCount(); + for (uint32_t v = 0; v < vertexCount; v++) { + halfedge::Vertex *vertex = m_chartMesh->vertexAt(v); + halfedge::Vertex *unifiedVertex = m_unifiedMesh->vertexAt(mapChartVertexToUnifiedVertex(v)); + vertex->tex = unifiedVertex->tex; + } + } + + float computeSurfaceArea() const { + return halfedge::computeSurfaceArea(m_chartMesh.get()) * scale; + } + + float computeParametricArea() const { + // This only makes sense in parameterized meshes. + xaDebugAssert(m_isDisk); + xaDebugAssert(!m_isVertexMapped); + return halfedge::computeParametricArea(m_chartMesh.get()); + } + + Vector2 computeParametricBounds() const { + // This only makes sense in parameterized meshes. + xaDebugAssert(m_isDisk); + xaDebugAssert(!m_isVertexMapped); + Box bounds; + bounds.clearBounds(); + uint32_t vertexCount = m_chartMesh->vertexCount(); + for (uint32_t v = 0; v < vertexCount; v++) { + halfedge::Vertex *vertex = m_chartMesh->vertexAt(v); + bounds.addPointToBounds(Vector3(vertex->tex, 0)); + } + return bounds.extents().xy(); + } + + float scale = 1.0f; + uint32_t vertexMapWidth; + uint32_t vertexMapHeight; + bool blockAligned = true; + +private: + bool closeLoop(uint32_t start, const std::vector<halfedge::Edge *> &loop) { + const uint32_t vertexCount = loop.size() - start; + xaDebugAssert(vertexCount >= 3); + if (vertexCount < 3) return false; + xaDebugAssert(loop[start]->vertex->isColocal(loop[start + vertexCount - 1]->to())); + // If the hole is planar, then we add a single face that will be properly triangulated later. + // If the hole is not planar, we add a triangle fan with a vertex at the hole centroid. + // This is still a bit of a hack. There surely are better hole filling algorithms out there. + std::vector<Vector3> points(vertexCount); + for (uint32_t i = 0; i < vertexCount; i++) { + points[i] = loop[start + i]->vertex->pos; + } + bool isPlanar = Fit::isPlanar(vertexCount, points.data()); + if (isPlanar) { + // Add face and connect edges. + halfedge::Face *face = m_unifiedMesh->addFace(); + for (uint32_t i = 0; i < vertexCount; i++) { + halfedge::Edge *edge = loop[start + i]; + edge->face = face; + edge->setNext(loop[start + (i + 1) % vertexCount]); + } + face->edge = loop[start]; + xaDebugAssert(face->isValid()); + } else { + // If the polygon is not planar, we just cross our fingers, and hope this will work: + // Compute boundary centroid: + Vector3 centroidPos(0); + for (uint32_t i = 0; i < vertexCount; i++) { + centroidPos += points[i]; + } + centroidPos *= (1.0f / vertexCount); + halfedge::Vertex *centroid = m_unifiedMesh->addVertex(centroidPos); + // Add one pair of edges for each boundary vertex. + for (uint32_t j = vertexCount - 1, i = 0; i < vertexCount; j = i++) { + halfedge::Face *face = m_unifiedMesh->addFace(centroid->id, loop[start + j]->vertex->id, loop[start + i]->vertex->id); + xaDebugAssert(face != NULL); +#ifdef NDEBUG + face = NULL; // silence unused parameter warning +#endif + } + } + return true; + } + + static void getBoundaryEdges(halfedge::Mesh *mesh, std::vector<halfedge::Edge *> &boundaryEdges) { + xaDebugAssert(mesh != NULL); + const uint32_t edgeCount = mesh->edgeCount(); + BitArray bitFlags(edgeCount); + bitFlags.clearAll(); + boundaryEdges.clear(); + // Search for boundary edges. Mark all the edges that belong to the same boundary. + for (uint32_t e = 0; e < edgeCount; e++) { + halfedge::Edge *startEdge = mesh->edgeAt(e); + if (startEdge != NULL && startEdge->isBoundary() && bitFlags.bitAt(e) == false) { + xaDebugAssert(startEdge->face != NULL); + xaDebugAssert(startEdge->pair->face == NULL); + startEdge = startEdge->pair; + const halfedge::Edge *edge = startEdge; + do { + xaDebugAssert(edge->face == NULL); + xaDebugAssert(bitFlags.bitAt(edge->id / 2) == false); + bitFlags.setBitAt(edge->id / 2); + edge = edge->next; + } while (startEdge != edge); + boundaryEdges.push_back(startEdge); + } + } + } + + // Chart mesh. + std::auto_ptr<halfedge::Mesh> m_chartMesh; + + std::auto_ptr<halfedge::Mesh> m_unifiedMesh; + bool m_isDisk; + bool m_isVertexMapped; + + // List of faces of the original mesh that belong to this chart. + std::vector<uint32_t> m_faceArray; + + // Map vertices of the chart mesh to vertices of the original mesh. + std::vector<uint32_t> m_chartToOriginalMap; + + std::vector<uint32_t> m_chartToUnifiedMap; +}; + +// Estimate quality of existing parameterization. +class ParameterizationQuality { +public: + ParameterizationQuality() { + m_totalTriangleCount = 0; + m_flippedTriangleCount = 0; + m_zeroAreaTriangleCount = 0; + m_parametricArea = 0.0f; + m_geometricArea = 0.0f; + m_stretchMetric = 0.0f; + m_maxStretchMetric = 0.0f; + m_conformalMetric = 0.0f; + m_authalicMetric = 0.0f; + } + + ParameterizationQuality(const halfedge::Mesh *mesh) { + xaDebugAssert(mesh != NULL); + m_totalTriangleCount = 0; + m_flippedTriangleCount = 0; + m_zeroAreaTriangleCount = 0; + m_parametricArea = 0.0f; + m_geometricArea = 0.0f; + m_stretchMetric = 0.0f; + m_maxStretchMetric = 0.0f; + m_conformalMetric = 0.0f; + m_authalicMetric = 0.0f; + const uint32_t faceCount = mesh->faceCount(); + for (uint32_t f = 0; f < faceCount; f++) { + const halfedge::Face *face = mesh->faceAt(f); + const halfedge::Vertex *vertex0 = NULL; + Vector3 p[3]; + Vector2 t[3]; + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Edge *edge = it.current(); + if (vertex0 == NULL) { + vertex0 = edge->vertex; + p[0] = vertex0->pos; + t[0] = vertex0->tex; + } else if (edge->to() != vertex0) { + p[1] = edge->from()->pos; + p[2] = edge->to()->pos; + t[1] = edge->from()->tex; + t[2] = edge->to()->tex; + processTriangle(p, t); + } + } + } + if (m_flippedTriangleCount + m_zeroAreaTriangleCount == faceCount) { + // If all triangles are flipped, then none is. + m_flippedTriangleCount = 0; + } + xaDebugAssert(std::isfinite(m_parametricArea) && m_parametricArea >= 0); + xaDebugAssert(std::isfinite(m_geometricArea) && m_geometricArea >= 0); + xaDebugAssert(std::isfinite(m_stretchMetric)); + xaDebugAssert(std::isfinite(m_maxStretchMetric)); + xaDebugAssert(std::isfinite(m_conformalMetric)); + xaDebugAssert(std::isfinite(m_authalicMetric)); + } + + bool isValid() const { + return m_flippedTriangleCount == 0; // @@ Does not test for self-overlaps. + } + + float rmsStretchMetric() const { + if (m_geometricArea == 0) return 0.0f; + float normFactor = sqrtf(m_parametricArea / m_geometricArea); + return sqrtf(m_stretchMetric / m_geometricArea) * normFactor; + } + + float maxStretchMetric() const { + if (m_geometricArea == 0) return 0.0f; + float normFactor = sqrtf(m_parametricArea / m_geometricArea); + return m_maxStretchMetric * normFactor; + } + + float rmsConformalMetric() const { + if (m_geometricArea == 0) return 0.0f; + return sqrtf(m_conformalMetric / m_geometricArea); + } + + float maxAuthalicMetric() const { + if (m_geometricArea == 0) return 0.0f; + return sqrtf(m_authalicMetric / m_geometricArea); + } + + void operator+=(const ParameterizationQuality &pq) { + m_totalTriangleCount += pq.m_totalTriangleCount; + m_flippedTriangleCount += pq.m_flippedTriangleCount; + m_zeroAreaTriangleCount += pq.m_zeroAreaTriangleCount; + m_parametricArea += pq.m_parametricArea; + m_geometricArea += pq.m_geometricArea; + m_stretchMetric += pq.m_stretchMetric; + m_maxStretchMetric = std::max(m_maxStretchMetric, pq.m_maxStretchMetric); + m_conformalMetric += pq.m_conformalMetric; + m_authalicMetric += pq.m_authalicMetric; + } + +private: + void processTriangle(Vector3 q[3], Vector2 p[3]) { + m_totalTriangleCount++; + // Evaluate texture stretch metric. See: + // - "Texture Mapping Progressive Meshes", Sander, Snyder, Gortler & Hoppe + // - "Mesh Parameterization: Theory and Practice", Siggraph'07 Course Notes, Hormann, Levy & Sheffer. + float t1 = p[0].x; + float s1 = p[0].y; + float t2 = p[1].x; + float s2 = p[1].y; + float t3 = p[2].x; + float s3 = p[2].y; + float geometricArea = length(cross(q[1] - q[0], q[2] - q[0])) / 2; + float parametricArea = ((s2 - s1) * (t3 - t1) - (s3 - s1) * (t2 - t1)) / 2; + if (isZero(parametricArea)) { + m_zeroAreaTriangleCount++; + return; + } + Vector3 Ss = (q[0] * (t2 - t3) + q[1] * (t3 - t1) + q[2] * (t1 - t2)) / (2 * parametricArea); + Vector3 St = (q[0] * (s3 - s2) + q[1] * (s1 - s3) + q[2] * (s2 - s1)) / (2 * parametricArea); + float a = dot(Ss, Ss); // E + float b = dot(Ss, St); // F + float c = dot(St, St); // G + // Compute eigen-values of the first fundamental form: + float sigma1 = sqrtf(0.5f * std::max(0.0f, a + c - sqrtf(square(a - c) + 4 * square(b)))); // gamma uppercase, min eigenvalue. + float sigma2 = sqrtf(0.5f * std::max(0.0f, a + c + sqrtf(square(a - c) + 4 * square(b)))); // gamma lowercase, max eigenvalue. + xaAssert(sigma2 >= sigma1); + // isometric: sigma1 = sigma2 = 1 + // conformal: sigma1 / sigma2 = 1 + // authalic: sigma1 * sigma2 = 1 + float rmsStretch = sqrtf((a + c) * 0.5f); + float rmsStretch2 = sqrtf((square(sigma1) + square(sigma2)) * 0.5f); + xaDebugAssert(equal(rmsStretch, rmsStretch2, 0.01f)); +#ifdef NDEBUG + rmsStretch2 = 0; // silence unused parameter warning +#endif + if (parametricArea < 0.0f) { + // Count flipped triangles. + m_flippedTriangleCount++; + parametricArea = fabsf(parametricArea); + } + m_stretchMetric += square(rmsStretch) * geometricArea; + m_maxStretchMetric = std::max(m_maxStretchMetric, sigma2); + if (!isZero(sigma1, 0.000001f)) { + // sigma1 is zero when geometricArea is zero. + m_conformalMetric += (sigma2 / sigma1) * geometricArea; + } + m_authalicMetric += (sigma1 * sigma2) * geometricArea; + // Accumulate total areas. + m_geometricArea += geometricArea; + m_parametricArea += parametricArea; + //triangleConformalEnergy(q, p); + } + + uint32_t m_totalTriangleCount; + uint32_t m_flippedTriangleCount; + uint32_t m_zeroAreaTriangleCount; + float m_parametricArea; + float m_geometricArea; + float m_stretchMetric; + float m_maxStretchMetric; + float m_conformalMetric; + float m_authalicMetric; +}; + +// Set of charts corresponding to a single mesh. +class MeshCharts { +public: + MeshCharts(const halfedge::Mesh *mesh) : + m_mesh(mesh) {} + + ~MeshCharts() { + for (size_t i = 0; i < m_chartArray.size(); i++) + delete m_chartArray[i]; + } + + uint32_t chartCount() const { + return m_chartArray.size(); + } + uint32_t vertexCount() const { + return m_totalVertexCount; + } + + const Chart *chartAt(uint32_t i) const { + return m_chartArray[i]; + } + Chart *chartAt(uint32_t i) { + return m_chartArray[i]; + } + + // Extract the charts of the input mesh. + void extractCharts() { + const uint32_t faceCount = m_mesh->faceCount(); + int first = 0; + std::vector<uint32_t> queue; + queue.reserve(faceCount); + BitArray bitFlags(faceCount); + bitFlags.clearAll(); + for (uint32_t f = 0; f < faceCount; f++) { + if (bitFlags.bitAt(f) == false) { + // Start new patch. Reset queue. + first = 0; + queue.clear(); + queue.push_back(f); + bitFlags.setBitAt(f); + while (first != (int)queue.size()) { + const halfedge::Face *face = m_mesh->faceAt(queue[first]); + // Visit face neighbors of queue[first] + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + const halfedge::Edge *edge = it.current(); + xaDebugAssert(edge->pair != NULL); + if (!edge->isBoundary() && /*!edge->isSeam()*/ + //!(edge->from()->tex() != edge->pair()->to()->tex() || edge->to()->tex() != edge->pair()->from()->tex())) + !(edge->from() != edge->pair->to() || edge->to() != edge->pair->from())) { // Preserve existing seams (not just texture seams). + const halfedge::Face *neighborFace = edge->pair->face; + xaDebugAssert(neighborFace != NULL); + if (bitFlags.bitAt(neighborFace->id) == false) { + queue.push_back(neighborFace->id); + bitFlags.setBitAt(neighborFace->id); + } + } + } + first++; + } + Chart *chart = new Chart(); + chart->build(m_mesh, queue); + m_chartArray.push_back(chart); + } + } + } + + /* + Compute charts using a simple segmentation algorithm. + + LSCM: + - identify sharp features using local dihedral angles. + - identify seed faces farthest from sharp features. + - grow charts from these seeds. + + MCGIM: + - phase 1: chart growth + - grow all charts simultaneously using dijkstra search on the dual graph of the mesh. + - graph edges are weighted based on planarity metric. + - metric uses distance to global chart normal. + - terminate when all faces have been assigned. + - phase 2: seed computation: + - place new seed of the chart at the most interior face. + - most interior is evaluated using distance metric only. + + - method repeates the two phases, until the location of the seeds does not change. + - cycles are detected by recording all the previous seeds and chartification terminates. + + D-Charts: + + - Uniaxial conic metric: + - N_c = axis of the generalized cone that best fits the chart. (cone can a be cylinder or a plane). + - omega_c = angle between the face normals and the axis. + - Fitting error between chart C and tringle t: F(c,t) = (N_c*n_t - cos(omega_c))^2 + + - Compactness metrics: + - Roundness: + - C(c,t) = pi * D(S_c,t)^2 / A_c + - S_c = chart seed. + - D(S_c,t) = length of the shortest path inside the chart betwen S_c and t. + - A_c = chart area. + - Straightness: + - P(c,t) = l_out(c,t) / l_in(c,t) + - l_out(c,t) = lenght of the edges not shared between C and t. + - l_in(c,t) = lenght of the edges shared between C and t. + + - Combined metric: + - Cost(c,t) = F(c,t)^alpha + C(c,t)^beta + P(c,t)^gamma + - alpha = 1, beta = 0.7, gamma = 0.5 + + Our basic approach: + - Just one iteration of k-means? + - Avoid dijkstra by greedily growing charts until a threshold is met. Increase threshold and repeat until no faces left. + - If distortion metric is too high, split chart, add two seeds. + - If chart size is low, try removing chart. + + Postprocess: + - If topology is not disk: + - Fill holes, if new faces fit proxy. + - Find best cut, otherwise. + - After parameterization: + - If boundary self-intersects: + - cut chart along the closest two diametral boundary vertices, repeat parametrization. + - what if the overlap is on an appendix? How do we find that out and cut appropiately? + - emphasize roundness metrics to prevent those cases. + - If interior self-overlaps: preserve boundary parameterization and use mean-value map. + */ + void computeCharts(const CharterOptions &options, const std::vector<uint32_t> &unchartedMaterialArray) { + Chart *vertexMap = NULL; + if (unchartedMaterialArray.size() != 0) { + vertexMap = new Chart(); + vertexMap->buildVertexMap(m_mesh, unchartedMaterialArray); + if (vertexMap->faceCount() == 0) { + delete vertexMap; + vertexMap = NULL; + } + } + AtlasBuilder builder(m_mesh); + if (vertexMap != NULL) { + // Mark faces that do not need to be charted. + builder.markUnchartedFaces(vertexMap->faceArray()); + m_chartArray.push_back(vertexMap); + } + if (builder.facesLeft != 0) { + // Tweak these values: + const float maxThreshold = 2; + const uint32_t growFaceCount = 32; + const uint32_t maxIterations = 4; + builder.options = options; + //builder.options.proxyFitMetricWeight *= 0.75; // relax proxy fit weight during initial seed placement. + //builder.options.roundnessMetricWeight = 0; + //builder.options.straightnessMetricWeight = 0; + // This seems a reasonable estimate. + uint32_t maxSeedCount = std::max(6U, builder.facesLeft); + // Create initial charts greedely. + xaPrint("### Placing seeds\n"); + builder.placeSeeds(maxThreshold, maxSeedCount); + xaPrint("### Placed %d seeds (max = %d)\n", builder.chartCount(), maxSeedCount); + builder.updateProxies(); + builder.mergeCharts(); +#if 1 + xaPrint("### Relocating seeds\n"); + builder.relocateSeeds(); + xaPrint("### Reset charts\n"); + builder.resetCharts(); + if (vertexMap != NULL) { + builder.markUnchartedFaces(vertexMap->faceArray()); + } + builder.options = options; + xaPrint("### Growing charts\n"); + // Restart process growing charts in parallel. + uint32_t iteration = 0; + while (true) { + if (!builder.growCharts(maxThreshold, growFaceCount)) { + xaPrint("### Can't grow anymore\n"); + // If charts cannot grow more: fill holes, merge charts, relocate seeds and start new iteration. + xaPrint("### Filling holes\n"); + builder.fillHoles(maxThreshold); + xaPrint("### Using %d charts now\n", builder.chartCount()); + builder.updateProxies(); + xaPrint("### Merging charts\n"); + builder.mergeCharts(); + xaPrint("### Using %d charts now\n", builder.chartCount()); + xaPrint("### Reseeding\n"); + if (!builder.relocateSeeds()) { + xaPrint("### Cannot relocate seeds anymore\n"); + // Done! + break; + } + if (iteration == maxIterations) { + xaPrint("### Reached iteration limit\n"); + break; + } + iteration++; + xaPrint("### Reset charts\n"); + builder.resetCharts(); + if (vertexMap != NULL) { + builder.markUnchartedFaces(vertexMap->faceArray()); + } + xaPrint("### Growing charts\n"); + } + }; +#endif + // Make sure no holes are left! + xaDebugAssert(builder.facesLeft == 0); + const uint32_t chartCount = builder.chartArray.size(); + for (uint32_t i = 0; i < chartCount; i++) { + Chart *chart = new Chart(); + m_chartArray.push_back(chart); + chart->build(m_mesh, builder.chartFaces(i)); + } + } + const uint32_t chartCount = m_chartArray.size(); + // Build face indices. + m_faceChart.resize(m_mesh->faceCount()); + m_faceIndex.resize(m_mesh->faceCount()); + for (uint32_t i = 0; i < chartCount; i++) { + const Chart *chart = m_chartArray[i]; + const uint32_t faceCount = chart->faceCount(); + for (uint32_t f = 0; f < faceCount; f++) { + uint32_t idx = chart->faceAt(f); + m_faceChart[idx] = i; + m_faceIndex[idx] = f; + } + } + // Build an exclusive prefix sum of the chart vertex counts. + m_chartVertexCountPrefixSum.resize(chartCount); + if (chartCount > 0) { + m_chartVertexCountPrefixSum[0] = 0; + for (uint32_t i = 1; i < chartCount; i++) { + const Chart *chart = m_chartArray[i - 1]; + m_chartVertexCountPrefixSum[i] = m_chartVertexCountPrefixSum[i - 1] + chart->vertexCount(); + } + m_totalVertexCount = m_chartVertexCountPrefixSum[chartCount - 1] + m_chartArray[chartCount - 1]->vertexCount(); + } else { + m_totalVertexCount = 0; + } + } + + void parameterizeCharts() { + ParameterizationQuality globalParameterizationQuality; + // Parameterize the charts. + uint32_t diskCount = 0; + const uint32_t chartCount = m_chartArray.size(); + for (uint32_t i = 0; i < chartCount; i++) { + Chart *chart = m_chartArray[i]; + + bool isValid = false; + + if (chart->isVertexMapped()) { + continue; + } + + if (chart->isDisk()) { + diskCount++; + ParameterizationQuality chartParameterizationQuality; + if (chart->faceCount() == 1) { + computeSingleFaceMap(chart->unifiedMesh()); + chartParameterizationQuality = ParameterizationQuality(chart->unifiedMesh()); + } else { + computeOrthogonalProjectionMap(chart->unifiedMesh()); + ParameterizationQuality orthogonalQuality(chart->unifiedMesh()); + computeLeastSquaresConformalMap(chart->unifiedMesh()); + ParameterizationQuality lscmQuality(chart->unifiedMesh()); + chartParameterizationQuality = lscmQuality; + } + isValid = chartParameterizationQuality.isValid(); + if (!isValid) { + xaPrint("*** Invalid parameterization.\n"); + } + // @@ Check that parameterization quality is above a certain threshold. + // @@ Detect boundary self-intersections. + globalParameterizationQuality += chartParameterizationQuality; + } + + // Transfer parameterization from unified mesh to chart mesh. + chart->transferParameterization(); + } + xaPrint(" Parameterized %d/%d charts.\n", diskCount, chartCount); + xaPrint(" RMS stretch metric: %f\n", globalParameterizationQuality.rmsStretchMetric()); + xaPrint(" MAX stretch metric: %f\n", globalParameterizationQuality.maxStretchMetric()); + xaPrint(" RMS conformal metric: %f\n", globalParameterizationQuality.rmsConformalMetric()); + xaPrint(" RMS authalic metric: %f\n", globalParameterizationQuality.maxAuthalicMetric()); + } + + uint32_t faceChartAt(uint32_t i) const { + return m_faceChart[i]; + } + uint32_t faceIndexWithinChartAt(uint32_t i) const { + return m_faceIndex[i]; + } + + uint32_t vertexCountBeforeChartAt(uint32_t i) const { + return m_chartVertexCountPrefixSum[i]; + } + +private: + const halfedge::Mesh *m_mesh; + + std::vector<Chart *> m_chartArray; + + std::vector<uint32_t> m_chartVertexCountPrefixSum; + uint32_t m_totalVertexCount; + + std::vector<uint32_t> m_faceChart; // the chart of every face of the input mesh. + std::vector<uint32_t> m_faceIndex; // the index within the chart for every face of the input mesh. +}; + +/// An atlas is a set of charts. +class Atlas { +public: + ~Atlas() { + for (size_t i = 0; i < m_meshChartsArray.size(); i++) + delete m_meshChartsArray[i]; + } + + uint32_t meshCount() const { + return m_meshChartsArray.size(); + } + + const MeshCharts *meshAt(uint32_t i) const { + return m_meshChartsArray[i]; + } + + MeshCharts *meshAt(uint32_t i) { + return m_meshChartsArray[i]; + } + + uint32_t chartCount() const { + uint32_t count = 0; + for (uint32_t c = 0; c < m_meshChartsArray.size(); c++) { + count += m_meshChartsArray[c]->chartCount(); + } + return count; + } + + const Chart *chartAt(uint32_t i) const { + for (uint32_t c = 0; c < m_meshChartsArray.size(); c++) { + uint32_t count = m_meshChartsArray[c]->chartCount(); + if (i < count) { + return m_meshChartsArray[c]->chartAt(i); + } + i -= count; + } + return NULL; + } + + Chart *chartAt(uint32_t i) { + for (uint32_t c = 0; c < m_meshChartsArray.size(); c++) { + uint32_t count = m_meshChartsArray[c]->chartCount(); + if (i < count) { + return m_meshChartsArray[c]->chartAt(i); + } + i -= count; + } + return NULL; + } + + // Add mesh charts and takes ownership. + // Extract the charts and add to this atlas. + void addMeshCharts(MeshCharts *meshCharts) { + m_meshChartsArray.push_back(meshCharts); + } + + void extractCharts(const halfedge::Mesh *mesh) { + MeshCharts *meshCharts = new MeshCharts(mesh); + meshCharts->extractCharts(); + addMeshCharts(meshCharts); + } + + void computeCharts(const halfedge::Mesh *mesh, const CharterOptions &options, const std::vector<uint32_t> &unchartedMaterialArray) { + MeshCharts *meshCharts = new MeshCharts(mesh); + meshCharts->computeCharts(options, unchartedMaterialArray); + addMeshCharts(meshCharts); + } + + void parameterizeCharts() { + for (uint32_t i = 0; i < m_meshChartsArray.size(); i++) { + m_meshChartsArray[i]->parameterizeCharts(); + } + } + +private: + std::vector<MeshCharts *> m_meshChartsArray; +}; + +struct AtlasPacker { + AtlasPacker(Atlas *atlas) : + m_atlas(atlas), + m_width(0), + m_height(0) { + // Save the original uvs. + m_originalChartUvs.resize(m_atlas->chartCount()); + for (uint32_t i = 0; i < m_atlas->chartCount(); i++) { + const halfedge::Mesh *mesh = atlas->chartAt(i)->chartMesh(); + m_originalChartUvs[i].resize(mesh->vertexCount()); + for (uint32_t j = 0; j < mesh->vertexCount(); j++) + m_originalChartUvs[i][j] = mesh->vertexAt(j)->tex; + } + } + + uint32_t getWidth() const { return m_width; } + uint32_t getHeight() const { return m_height; } + + // Pack charts in the smallest possible rectangle. + void packCharts(const PackerOptions &options) { + const uint32_t chartCount = m_atlas->chartCount(); + if (chartCount == 0) return; + float texelsPerUnit = 1; + if (options.method == PackMethod::TexelArea) + texelsPerUnit = options.texelArea; + for (int iteration = 0;; iteration++) { + m_rand = MTRand(); + std::vector<float> chartOrderArray(chartCount); + std::vector<Vector2> chartExtents(chartCount); + float meshArea = 0; + for (uint32_t c = 0; c < chartCount; c++) { + Chart *chart = m_atlas->chartAt(c); + if (!chart->isVertexMapped() && !chart->isDisk()) { + chartOrderArray[c] = 0; + // Skip non-disks. + continue; + } + Vector2 extents(0.0f); + if (chart->isVertexMapped()) { + // Arrange vertices in a rectangle. + extents.x = float(chart->vertexMapWidth); + extents.y = float(chart->vertexMapHeight); + } else { + // Compute surface area to sort charts. + float chartArea = chart->computeSurfaceArea(); + meshArea += chartArea; + //chartOrderArray[c] = chartArea; + // Compute chart scale + float parametricArea = fabsf(chart->computeParametricArea()); // @@ There doesn't seem to be anything preventing parametric area to be negative. + if (parametricArea < NV_EPSILON) { + // When the parametric area is too small we use a rough approximation to prevent divisions by very small numbers. + Vector2 bounds = chart->computeParametricBounds(); + parametricArea = bounds.x * bounds.y; + } + float scale = (chartArea / parametricArea) * texelsPerUnit; + if (parametricArea == 0) { // < NV_EPSILON) + scale = 0; + } + xaAssert(std::isfinite(scale)); + // Compute bounding box of chart. + Vector2 majorAxis, minorAxis, origin, end; + computeBoundingBox(chart, &majorAxis, &minorAxis, &origin, &end); + xaAssert(isFinite(majorAxis) && isFinite(minorAxis) && isFinite(origin)); + // Sort charts by perimeter. @@ This is sometimes producing somewhat unexpected results. Is this right? + //chartOrderArray[c] = ((end.x - origin.x) + (end.y - origin.y)) * scale; + // Translate, rotate and scale vertices. Compute extents. + halfedge::Mesh *mesh = chart->chartMesh(); + const uint32_t vertexCount = mesh->vertexCount(); + for (uint32_t i = 0; i < vertexCount; i++) { + halfedge::Vertex *vertex = mesh->vertexAt(i); + //Vector2 t = vertex->tex - origin; + Vector2 tmp; + tmp.x = dot(vertex->tex, majorAxis); + tmp.y = dot(vertex->tex, minorAxis); + tmp -= origin; + tmp *= scale; + if (tmp.x < 0 || tmp.y < 0) { + xaPrint("tmp: %f %f\n", tmp.x, tmp.y); + xaPrint("scale: %f\n", scale); + xaPrint("origin: %f %f\n", origin.x, origin.y); + xaPrint("majorAxis: %f %f\n", majorAxis.x, majorAxis.y); + xaPrint("minorAxis: %f %f\n", minorAxis.x, minorAxis.y); + xaDebugAssert(false); + } + //xaAssert(tmp.x >= 0 && tmp.y >= 0); + vertex->tex = tmp; + xaAssert(std::isfinite(vertex->tex.x) && std::isfinite(vertex->tex.y)); + extents = max(extents, tmp); + } + xaDebugAssert(extents.x >= 0 && extents.y >= 0); + // Limit chart size. + if (extents.x > 1024 || extents.y > 1024) { + float limit = std::max(extents.x, extents.y); + scale = 1024 / (limit + 1); + for (uint32_t i = 0; i < vertexCount; i++) { + halfedge::Vertex *vertex = mesh->vertexAt(i); + vertex->tex *= scale; + } + extents *= scale; + xaDebugAssert(extents.x <= 1024 && extents.y <= 1024); + } + // Scale the charts to use the entire texel area available. So, if the width is 0.1 we could scale it to 1 without increasing the lightmap usage and making a better + // use of it. In many cases this also improves the look of the seams, since vertices on the chart boundaries have more chances of being aligned with the texel centers. + float scale_x = 1.0f; + float scale_y = 1.0f; + float divide_x = 1.0f; + float divide_y = 1.0f; + if (extents.x > 0) { + int cw = ftoi_ceil(extents.x); + if (options.blockAlign && chart->blockAligned) { + // Align all chart extents to 4x4 blocks, but taking padding into account. + if (options.conservative) { + cw = align(cw + 2, 4) - 2; + } else { + cw = align(cw + 1, 4) - 1; + } + } + scale_x = (float(cw) - NV_EPSILON); + divide_x = extents.x; + extents.x = float(cw); + } + if (extents.y > 0) { + int ch = ftoi_ceil(extents.y); + if (options.blockAlign && chart->blockAligned) { + // Align all chart extents to 4x4 blocks, but taking padding into account. + if (options.conservative) { + ch = align(ch + 2, 4) - 2; + } else { + ch = align(ch + 1, 4) - 1; + } + } + scale_y = (float(ch) - NV_EPSILON); + divide_y = extents.y; + extents.y = float(ch); + } + for (uint32_t v = 0; v < vertexCount; v++) { + halfedge::Vertex *vertex = mesh->vertexAt(v); + vertex->tex.x /= divide_x; + vertex->tex.y /= divide_y; + vertex->tex.x *= scale_x; + vertex->tex.y *= scale_y; + xaAssert(std::isfinite(vertex->tex.x) && std::isfinite(vertex->tex.y)); + } + } + chartExtents[c] = extents; + // Sort charts by perimeter. + chartOrderArray[c] = extents.x + extents.y; + } + // @@ We can try to improve compression of small charts by sorting them by proximity like we do with vertex samples. + // @@ How to do that? One idea: compute chart centroid, insert into grid, compute morton index of the cell, sort based on morton index. + // @@ We would sort by morton index, first, then quantize the chart sizes, so that all small charts have the same size, and sort by size preserving the morton order. + //xaPrint("Sorting charts.\n"); + // Sort charts by area. + m_radix = RadixSort(); + m_radix.sort(chartOrderArray); + const uint32_t *ranks = m_radix.ranks(); + // First iteration - guess texelsPerUnit. + if (options.method != PackMethod::TexelArea && iteration == 0) { + // Estimate size of the map based on the mesh surface area and given texel scale. + const float texelCount = std::max(1.0f, meshArea * square(texelsPerUnit) / 0.75f); // Assume 75% utilization. + texelsPerUnit = sqrt((options.resolution * options.resolution) / texelCount); + resetUvs(); + continue; + } + // Init bit map. + m_bitmap.clearAll(); + m_bitmap.resize(options.resolution, options.resolution, false); + int w = 0; + int h = 0; + // Add sorted charts to bitmap. + for (uint32_t i = 0; i < chartCount; i++) { + uint32_t c = ranks[chartCount - i - 1]; // largest chart first + Chart *chart = m_atlas->chartAt(c); + if (!chart->isVertexMapped() && !chart->isDisk()) continue; + //float scale_x = 1; + //float scale_y = 1; + BitMap chart_bitmap; + if (chart->isVertexMapped()) { + chart->blockAligned = false; + // Init all bits to 1. + chart_bitmap.resize(ftoi_ceil(chartExtents[c].x), ftoi_ceil(chartExtents[c].y), /*initValue=*/true); + // @@ Another alternative would be to try to map each vertex to a different texel trying to fill all the available unused texels. + } else { + // @@ Add special cases for dot and line charts. @@ Lightmap rasterizer also needs to handle these special cases. + // @@ We could also have a special case for chart quads. If the quad surface <= 4 texels, align vertices with texel centers and do not add padding. May be very useful for foliage. + // @@ In general we could reduce the padding of all charts by one texel by using a rasterizer that takes into account the 2-texel footprint of the tent bilinear filter. For example, + // if we have a chart that is less than 1 texel wide currently we add one texel to the left and one texel to the right creating a 3-texel-wide bitmap. However, if we know that the + // chart is only 1 texel wide we could align it so that it only touches the footprint of two texels: + // | | <- Touches texels 0, 1 and 2. + // | | <- Only touches texels 0 and 1. + // \ \ / \ / / + // \ X X / + // \ / \ / \ / + // V V V + // 0 1 2 + if (options.conservative) { + // Init all bits to 0. + chart_bitmap.resize(ftoi_ceil(chartExtents[c].x) + 1 + options.padding, ftoi_ceil(chartExtents[c].y) + 1 + options.padding, /*initValue=*/false); // + 2 to add padding on both sides. + // Rasterize chart and dilate. + drawChartBitmapDilate(chart, &chart_bitmap, options.padding); + } else { + // Init all bits to 0. + chart_bitmap.resize(ftoi_ceil(chartExtents[c].x) + 1, ftoi_ceil(chartExtents[c].y) + 1, /*initValue=*/false); // Add half a texels on each side. + // Rasterize chart and dilate. + drawChartBitmap(chart, &chart_bitmap, Vector2(1), Vector2(0.5)); + } + } + int best_x, best_y; + int best_cw, best_ch; // Includes padding now. + int best_r; + findChartLocation(options.quality, &chart_bitmap, chartExtents[c], w, h, &best_x, &best_y, &best_cw, &best_ch, &best_r, chart->blockAligned); + /*if (w < best_x + best_cw || h < best_y + best_ch) + { + xaPrint("Resize extents to (%d, %d).\n", best_x + best_cw, best_y + best_ch); + }*/ + // Update parametric extents. + w = std::max(w, best_x + best_cw); + h = std::max(h, best_y + best_ch); + w = align(w, 4); + h = align(h, 4); + // Resize bitmap if necessary. + if (uint32_t(w) > m_bitmap.width() || uint32_t(h) > m_bitmap.height()) { + //xaPrint("Resize bitmap (%d, %d).\n", nextPowerOfTwo(w), nextPowerOfTwo(h)); + m_bitmap.resize(nextPowerOfTwo(uint32_t(w)), nextPowerOfTwo(uint32_t(h)), false); + } + //xaPrint("Add chart at (%d, %d).\n", best_x, best_y); + addChart(&chart_bitmap, w, h, best_x, best_y, best_r); + //float best_angle = 2 * PI * best_r; + // Translate and rotate chart texture coordinates. + halfedge::Mesh *mesh = chart->chartMesh(); + const uint32_t vertexCount = mesh->vertexCount(); + for (uint32_t v = 0; v < vertexCount; v++) { + halfedge::Vertex *vertex = mesh->vertexAt(v); + Vector2 t = vertex->tex; + if (best_r) std::swap(t.x, t.y); + //vertex->tex.x = best_x + t.x * cosf(best_angle) - t.y * sinf(best_angle); + //vertex->tex.y = best_y + t.x * sinf(best_angle) + t.y * cosf(best_angle); + vertex->tex.x = best_x + t.x + 0.5f; + vertex->tex.y = best_y + t.y + 0.5f; + xaAssert(vertex->tex.x >= 0 && vertex->tex.y >= 0); + xaAssert(std::isfinite(vertex->tex.x) && std::isfinite(vertex->tex.y)); + } + } + //w -= padding - 1; // Leave one pixel border! + //h -= padding - 1; + m_width = std::max(0, w); + m_height = std::max(0, h); + xaAssert(isAligned(m_width, 4)); + xaAssert(isAligned(m_height, 4)); + if (options.method == PackMethod::ExactResolution) { + texelsPerUnit *= sqrt((options.resolution * options.resolution) / (float)(m_width * m_height)); + if (iteration > 1 && m_width <= options.resolution && m_height <= options.resolution) { + m_width = m_height = options.resolution; + return; + } + resetUvs(); + } else { + return; + } + } + } + + float computeAtlasUtilization() const { + const uint32_t w = m_width; + const uint32_t h = m_height; + xaDebugAssert(w <= m_bitmap.width()); + xaDebugAssert(h <= m_bitmap.height()); + uint32_t count = 0; + for (uint32_t y = 0; y < h; y++) { + for (uint32_t x = 0; x < w; x++) { + count += m_bitmap.bitAt(x, y); + } + } + return float(count) / (w * h); + } + +private: + void resetUvs() { + for (uint32_t i = 0; i < m_atlas->chartCount(); i++) { + halfedge::Mesh *mesh = m_atlas->chartAt(i)->chartMesh(); + for (uint32_t j = 0; j < mesh->vertexCount(); j++) + mesh->vertexAt(j)->tex = m_originalChartUvs[i][j]; + } + } + + // IC: Brute force is slow, and random may take too much time to converge. We start inserting large charts in a small atlas. Using brute force is lame, because most of the space + // is occupied at this point. At the end we have many small charts and a large atlas with sparse holes. Finding those holes randomly is slow. A better approach would be to + // start stacking large charts as if they were tetris pieces. Once charts get small try to place them randomly. It may be interesting to try a intermediate strategy, first try + // along one axis and then try exhaustively along that axis. + void findChartLocation(int quality, const BitMap *bitmap, Vector2::Arg extents, int w, int h, int *best_x, int *best_y, int *best_w, int *best_h, int *best_r, bool blockAligned) { + int attempts = 256; + if (quality == 1) attempts = 4096; + if (quality == 2) attempts = 2048; + if (quality == 3) attempts = 1024; + if (quality == 4) attempts = 512; + if (quality == 0 || w * h < attempts) { + findChartLocation_bruteForce(bitmap, extents, w, h, best_x, best_y, best_w, best_h, best_r, blockAligned); + } else { + findChartLocation_random(bitmap, extents, w, h, best_x, best_y, best_w, best_h, best_r, attempts, blockAligned); + } + } + + void findChartLocation_bruteForce(const BitMap *bitmap, Vector2::Arg /*extents*/, int w, int h, int *best_x, int *best_y, int *best_w, int *best_h, int *best_r, bool blockAligned) { + const int BLOCK_SIZE = 4; + int best_metric = INT_MAX; + int step_size = blockAligned ? BLOCK_SIZE : 1; + // Try two different orientations. + for (int r = 0; r < 2; r++) { + int cw = bitmap->width(); + int ch = bitmap->height(); + if (r & 1) std::swap(cw, ch); + for (int y = 0; y <= h + 1; y += step_size) { // + 1 to extend atlas in case atlas full. + for (int x = 0; x <= w + 1; x += step_size) { // + 1 not really necessary here. + // Early out. + int area = std::max(w, x + cw) * std::max(h, y + ch); + //int perimeter = max(w, x+cw) + max(h, y+ch); + int extents = std::max(std::max(w, x + cw), std::max(h, y + ch)); + int metric = extents * extents + area; + if (metric > best_metric) { + continue; + } + if (metric == best_metric && std::max(x, y) >= std::max(*best_x, *best_y)) { + // If metric is the same, pick the one closest to the origin. + continue; + } + if (canAddChart(bitmap, w, h, x, y, r)) { + best_metric = metric; + *best_x = x; + *best_y = y; + *best_w = cw; + *best_h = ch; + *best_r = r; + if (area == w * h) { + // Chart is completely inside, do not look at any other location. + goto done; + } + } + } + } + } + done: + xaDebugAssert(best_metric != INT_MAX); + } + + void findChartLocation_random(const BitMap *bitmap, Vector2::Arg /*extents*/, int w, int h, int *best_x, int *best_y, int *best_w, int *best_h, int *best_r, int minTrialCount, bool blockAligned) { + const int BLOCK_SIZE = 4; + int best_metric = INT_MAX; + for (int i = 0; i < minTrialCount || best_metric == INT_MAX; i++) { + int r = m_rand.getRange(1); + int x = m_rand.getRange(w + 1); // + 1 to extend atlas in case atlas full. We may want to use a higher number to increase probability of extending atlas. + int y = m_rand.getRange(h + 1); // + 1 to extend atlas in case atlas full. + if (blockAligned) { + x = align(x, BLOCK_SIZE); + y = align(y, BLOCK_SIZE); + } + int cw = bitmap->width(); + int ch = bitmap->height(); + if (r & 1) std::swap(cw, ch); + // Early out. + int area = std::max(w, x + cw) * std::max(h, y + ch); + //int perimeter = max(w, x+cw) + max(h, y+ch); + int extents = std::max(std::max(w, x + cw), std::max(h, y + ch)); + int metric = extents * extents + area; + if (metric > best_metric) { + continue; + } + if (metric == best_metric && std::min(x, y) > std::min(*best_x, *best_y)) { + // If metric is the same, pick the one closest to the origin. + continue; + } + if (canAddChart(bitmap, w, h, x, y, r)) { + best_metric = metric; + *best_x = x; + *best_y = y; + *best_w = cw; + *best_h = ch; + *best_r = r; + if (area == w * h) { + // Chart is completely inside, do not look at any other location. + break; + } + } + } + } + + void drawChartBitmapDilate(const Chart *chart, BitMap *bitmap, int padding) { + const int w = bitmap->width(); + const int h = bitmap->height(); + const Vector2 extents = Vector2(float(w), float(h)); + // Rasterize chart faces, check that all bits are not set. + const uint32_t faceCount = chart->faceCount(); + for (uint32_t f = 0; f < faceCount; f++) { + const halfedge::Face *face = chart->chartMesh()->faceAt(f); + Vector2 vertices[4]; + uint32_t edgeCount = 0; + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + if (edgeCount < 4) { + vertices[edgeCount] = it.vertex()->tex + Vector2(0.5) + Vector2(float(padding), float(padding)); + } + edgeCount++; + } + if (edgeCount == 3) { + raster::drawTriangle(raster::Mode_Antialiased, extents, true, vertices, AtlasPacker::setBitsCallback, bitmap); + } else { + raster::drawQuad(raster::Mode_Antialiased, extents, true, vertices, AtlasPacker::setBitsCallback, bitmap); + } + } + // Expand chart by padding pixels. (dilation) + BitMap tmp(w, h); + for (int i = 0; i < padding; i++) { + tmp.clearAll(); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + bool b = bitmap->bitAt(x, y); + if (!b) { + if (x > 0) { + b |= bitmap->bitAt(x - 1, y); + if (y > 0) b |= bitmap->bitAt(x - 1, y - 1); + if (y < h - 1) b |= bitmap->bitAt(x - 1, y + 1); + } + if (y > 0) b |= bitmap->bitAt(x, y - 1); + if (y < h - 1) b |= bitmap->bitAt(x, y + 1); + if (x < w - 1) { + b |= bitmap->bitAt(x + 1, y); + if (y > 0) b |= bitmap->bitAt(x + 1, y - 1); + if (y < h - 1) b |= bitmap->bitAt(x + 1, y + 1); + } + } + if (b) tmp.setBitAt(x, y); + } + } + std::swap(tmp, *bitmap); + } + } + + void drawChartBitmap(const Chart *chart, BitMap *bitmap, const Vector2 &scale, const Vector2 &offset) { + const int w = bitmap->width(); + const int h = bitmap->height(); + const Vector2 extents = Vector2(float(w), float(h)); + static const Vector2 pad[4] = { + Vector2(-0.5, -0.5), + Vector2(0.5, -0.5), + Vector2(-0.5, 0.5), + Vector2(0.5, 0.5) + }; + // Rasterize 4 times to add proper padding. + for (int i = 0; i < 4; i++) { + // Rasterize chart faces, check that all bits are not set. + const uint32_t faceCount = chart->chartMesh()->faceCount(); + for (uint32_t f = 0; f < faceCount; f++) { + const halfedge::Face *face = chart->chartMesh()->faceAt(f); + Vector2 vertices[4]; + uint32_t edgeCount = 0; + for (halfedge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { + if (edgeCount < 4) { + vertices[edgeCount] = it.vertex()->tex * scale + offset + pad[i]; + xaAssert(ftoi_ceil(vertices[edgeCount].x) >= 0); + xaAssert(ftoi_ceil(vertices[edgeCount].y) >= 0); + xaAssert(ftoi_ceil(vertices[edgeCount].x) <= w); + xaAssert(ftoi_ceil(vertices[edgeCount].y) <= h); + } + edgeCount++; + } + if (edgeCount == 3) { + raster::drawTriangle(raster::Mode_Antialiased, extents, /*enableScissors=*/true, vertices, AtlasPacker::setBitsCallback, bitmap); + } else { + raster::drawQuad(raster::Mode_Antialiased, extents, /*enableScissors=*/true, vertices, AtlasPacker::setBitsCallback, bitmap); + } + } + } + // Expand chart by padding pixels. (dilation) + BitMap tmp(w, h); + tmp.clearAll(); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + bool b = bitmap->bitAt(x, y); + if (!b) { + if (x > 0) { + b |= bitmap->bitAt(x - 1, y); + if (y > 0) b |= bitmap->bitAt(x - 1, y - 1); + if (y < h - 1) b |= bitmap->bitAt(x - 1, y + 1); + } + if (y > 0) b |= bitmap->bitAt(x, y - 1); + if (y < h - 1) b |= bitmap->bitAt(x, y + 1); + if (x < w - 1) { + b |= bitmap->bitAt(x + 1, y); + if (y > 0) b |= bitmap->bitAt(x + 1, y - 1); + if (y < h - 1) b |= bitmap->bitAt(x + 1, y + 1); + } + } + if (b) tmp.setBitAt(x, y); + } + } + std::swap(tmp, *bitmap); + } + + bool canAddChart(const BitMap *bitmap, int atlas_w, int atlas_h, int offset_x, int offset_y, int r) { + xaDebugAssert(r == 0 || r == 1); + // Check whether the two bitmaps overlap. + const int w = bitmap->width(); + const int h = bitmap->height(); + if (r == 0) { + for (int y = 0; y < h; y++) { + int yy = y + offset_y; + if (yy >= 0) { + for (int x = 0; x < w; x++) { + int xx = x + offset_x; + if (xx >= 0) { + if (bitmap->bitAt(x, y)) { + if (xx < atlas_w && yy < atlas_h) { + if (m_bitmap.bitAt(xx, yy)) return false; + } + } + } + } + } + } + } else if (r == 1) { + for (int y = 0; y < h; y++) { + int xx = y + offset_x; + if (xx >= 0) { + for (int x = 0; x < w; x++) { + int yy = x + offset_y; + if (yy >= 0) { + if (bitmap->bitAt(x, y)) { + if (xx < atlas_w && yy < atlas_h) { + if (m_bitmap.bitAt(xx, yy)) return false; + } + } + } + } + } + } + } + return true; + } + + void addChart(const BitMap *bitmap, int atlas_w, int atlas_h, int offset_x, int offset_y, int r) { + xaDebugAssert(r == 0 || r == 1); + // Check whether the two bitmaps overlap. + const int w = bitmap->width(); + const int h = bitmap->height(); + if (r == 0) { + for (int y = 0; y < h; y++) { + int yy = y + offset_y; + if (yy >= 0) { + for (int x = 0; x < w; x++) { + int xx = x + offset_x; + if (xx >= 0) { + if (bitmap->bitAt(x, y)) { + if (xx < atlas_w && yy < atlas_h) { + xaDebugAssert(m_bitmap.bitAt(xx, yy) == false); + m_bitmap.setBitAt(xx, yy); + } + } + } + } + } + } + } else if (r == 1) { + for (int y = 0; y < h; y++) { + int xx = y + offset_x; + if (xx >= 0) { + for (int x = 0; x < w; x++) { + int yy = x + offset_y; + if (yy >= 0) { + if (bitmap->bitAt(x, y)) { + if (xx < atlas_w && yy < atlas_h) { + xaDebugAssert(m_bitmap.bitAt(xx, yy) == false); + m_bitmap.setBitAt(xx, yy); + } + } + } + } + } + } + } + } + + static bool setBitsCallback(void *param, int x, int y, Vector3::Arg, Vector3::Arg, Vector3::Arg, float area) { + BitMap *bitmap = (BitMap *)param; + if (area > 0.0) { + bitmap->setBitAt(x, y); + } + return true; + } + + // Compute the convex hull using Graham Scan. + static void convexHull(const std::vector<Vector2> &input, std::vector<Vector2> &output, float epsilon) { + const uint32_t inputCount = input.size(); + std::vector<float> coords(inputCount); + for (uint32_t i = 0; i < inputCount; i++) { + coords[i] = input[i].x; + } + RadixSort radix; + radix.sort(coords); + const uint32_t *ranks = radix.ranks(); + std::vector<Vector2> top; + top.reserve(inputCount); + std::vector<Vector2> bottom; + bottom.reserve(inputCount); + Vector2 P = input[ranks[0]]; + Vector2 Q = input[ranks[inputCount - 1]]; + float topy = std::max(P.y, Q.y); + float boty = std::min(P.y, Q.y); + for (uint32_t i = 0; i < inputCount; i++) { + Vector2 p = input[ranks[i]]; + if (p.y >= boty) top.push_back(p); + } + for (uint32_t i = 0; i < inputCount; i++) { + Vector2 p = input[ranks[inputCount - 1 - i]]; + if (p.y <= topy) bottom.push_back(p); + } + // Filter top list. + output.clear(); + output.push_back(top[0]); + output.push_back(top[1]); + for (uint32_t i = 2; i < top.size();) { + Vector2 a = output[output.size() - 2]; + Vector2 b = output[output.size() - 1]; + Vector2 c = top[i]; + float area = triangleArea(a, b, c); + if (area >= -epsilon) { + output.pop_back(); + } + if (area < -epsilon || output.size() == 1) { + output.push_back(c); + i++; + } + } + uint32_t top_count = output.size(); + output.push_back(bottom[1]); + // Filter bottom list. + for (uint32_t i = 2; i < bottom.size();) { + Vector2 a = output[output.size() - 2]; + Vector2 b = output[output.size() - 1]; + Vector2 c = bottom[i]; + float area = triangleArea(a, b, c); + if (area >= -epsilon) { + output.pop_back(); + } + if (area < -epsilon || output.size() == top_count) { + output.push_back(c); + i++; + } + } + // Remove duplicate element. + xaDebugAssert(output.front() == output.back()); + output.pop_back(); + } + + // This should compute convex hull and use rotating calipers to find the best box. Currently it uses a brute force method. + static void computeBoundingBox(Chart *chart, Vector2 *majorAxis, Vector2 *minorAxis, Vector2 *minCorner, Vector2 *maxCorner) { + // Compute list of boundary points. + std::vector<Vector2> points; + points.reserve(16); + halfedge::Mesh *mesh = chart->chartMesh(); + const uint32_t vertexCount = mesh->vertexCount(); + for (uint32_t i = 0; i < vertexCount; i++) { + halfedge::Vertex *vertex = mesh->vertexAt(i); + if (vertex->isBoundary()) { + points.push_back(vertex->tex); + } + } + xaDebugAssert(points.size() > 0); + std::vector<Vector2> hull; + convexHull(points, hull, 0.00001f); + // @@ Ideally I should use rotating calipers to find the best box. Using brute force for now. + float best_area = FLT_MAX; + Vector2 best_min; + Vector2 best_max; + Vector2 best_axis; + const uint32_t hullCount = hull.size(); + for (uint32_t i = 0, j = hullCount - 1; i < hullCount; j = i, i++) { + if (equal(hull[i], hull[j])) { + continue; + } + Vector2 axis = normalize(hull[i] - hull[j], 0.0f); + xaDebugAssert(isFinite(axis)); + // Compute bounding box. + Vector2 box_min(FLT_MAX, FLT_MAX); + Vector2 box_max(-FLT_MAX, -FLT_MAX); + for (uint32_t v = 0; v < hullCount; v++) { + Vector2 point = hull[v]; + float x = dot(axis, point); + if (x < box_min.x) box_min.x = x; + if (x > box_max.x) box_max.x = x; + float y = dot(Vector2(-axis.y, axis.x), point); + if (y < box_min.y) box_min.y = y; + if (y > box_max.y) box_max.y = y; + } + // Compute box area. + float area = (box_max.x - box_min.x) * (box_max.y - box_min.y); + if (area < best_area) { + best_area = area; + best_min = box_min; + best_max = box_max; + best_axis = axis; + } + } + // Consider all points, not only boundary points, in case the input chart is malformed. + for (uint32_t i = 0; i < vertexCount; i++) { + halfedge::Vertex *vertex = mesh->vertexAt(i); + Vector2 point = vertex->tex; + float x = dot(best_axis, point); + if (x < best_min.x) best_min.x = x; + if (x > best_max.x) best_max.x = x; + float y = dot(Vector2(-best_axis.y, best_axis.x), point); + if (y < best_min.y) best_min.y = y; + if (y > best_max.y) best_max.y = y; + } + *majorAxis = best_axis; + *minorAxis = Vector2(-best_axis.y, best_axis.x); + *minCorner = best_min; + *maxCorner = best_max; + } + + Atlas *m_atlas; + BitMap m_bitmap; + RadixSort m_radix; + uint32_t m_width; + uint32_t m_height; + MTRand m_rand; + std::vector<std::vector<Vector2> > m_originalChartUvs; +}; + +} // namespace param +} // namespace internal + +struct Atlas { + internal::param::Atlas atlas; + std::vector<internal::halfedge::Mesh *> heMeshes; + uint32_t width = 0; + uint32_t height = 0; + OutputMesh **outputMeshes = NULL; +}; + +void SetPrint(PrintFunc print) { + internal::s_print = print; +} + +Atlas *Create() { + Atlas *atlas = new Atlas(); + return atlas; +} + +void Destroy(Atlas *atlas) { + xaAssert(atlas); + for (int i = 0; i < (int)atlas->heMeshes.size(); i++) { + delete atlas->heMeshes[i]; + if (atlas->outputMeshes) { + OutputMesh *outputMesh = atlas->outputMeshes[i]; + for (uint32_t j = 0; j < outputMesh->chartCount; j++) + delete[] outputMesh->chartArray[j].indexArray; + delete[] outputMesh->chartArray; + delete[] outputMesh->vertexArray; + delete[] outputMesh->indexArray; + delete outputMesh; + } + } + delete[] atlas->outputMeshes; + delete atlas; +} + +static internal::Vector3 DecodePosition(const InputMesh &mesh, uint32_t index) { + xaAssert(mesh.vertexPositionData); + return *((const internal::Vector3 *)&((const uint8_t *)mesh.vertexPositionData)[mesh.vertexPositionStride * index]); +} + +static internal::Vector3 DecodeNormal(const InputMesh &mesh, uint32_t index) { + xaAssert(mesh.vertexNormalData); + return *((const internal::Vector3 *)&((const uint8_t *)mesh.vertexNormalData)[mesh.vertexNormalStride * index]); +} + +static internal::Vector2 DecodeUv(const InputMesh &mesh, uint32_t index) { + xaAssert(mesh.vertexUvData); + return *((const internal::Vector2 *)&((const uint8_t *)mesh.vertexUvData)[mesh.vertexUvStride * index]); +} + +static uint32_t DecodeIndex(IndexFormat::Enum format, const void *indexData, uint32_t i) { + if (format == IndexFormat::HalfFloat) + return (uint32_t)((const uint16_t *)indexData)[i]; + return ((const uint32_t *)indexData)[i]; +} + +static float EdgeLength(internal::Vector3 pos1, internal::Vector3 pos2) { + return internal::length(pos2 - pos1); +} + +AddMeshError AddMesh(Atlas *atlas, const InputMesh &mesh, bool useColocalVertices) { + xaAssert(atlas); + AddMeshError error; + error.code = AddMeshErrorCode::Success; + error.face = error.index0 = error.index1 = UINT32_MAX; + // Expecting triangle faces. + if ((mesh.indexCount % 3) != 0) { + error.code = AddMeshErrorCode::InvalidIndexCount; + return error; + } + // Check if any index is out of range. + for (uint32_t j = 0; j < mesh.indexCount; j++) { + const uint32_t index = DecodeIndex(mesh.indexFormat, mesh.indexData, j); + if (index < 0 || index >= mesh.vertexCount) { + error.code = AddMeshErrorCode::IndexOutOfRange; + error.index0 = index; + return error; + } + } + // Build half edge mesh. + internal::halfedge::Mesh *heMesh = new internal::halfedge::Mesh; + std::vector<uint32_t> canonicalMap; + canonicalMap.reserve(mesh.vertexCount); + for (uint32_t i = 0; i < mesh.vertexCount; i++) { + internal::halfedge::Vertex *vertex = heMesh->addVertex(DecodePosition(mesh, i)); + if (mesh.vertexNormalData) + vertex->nor = DecodeNormal(mesh, i); + if (mesh.vertexUvData) + vertex->tex = DecodeUv(mesh, i); + // Link colocals. You probably want to do this more efficiently! Sort by one axis or use a hash or grid. + uint32_t firstColocal = i; + if (useColocalVertices) { + for (uint32_t j = 0; j < i; j++) { + if (vertex->pos != DecodePosition(mesh, j)) + continue; +#if 0 + if (mesh.vertexNormalData && vertex->nor != DecodeNormal(mesh, j)) + continue; +#endif + if (mesh.vertexUvData && vertex->tex != DecodeUv(mesh, j)) + continue; + firstColocal = j; + break; + } + } + canonicalMap.push_back(firstColocal); + } + heMesh->linkColocalsWithCanonicalMap(canonicalMap); + for (uint32_t i = 0; i < mesh.indexCount / 3; i++) { + uint32_t tri[3]; + for (int j = 0; j < 3; j++) + tri[j] = DecodeIndex(mesh.indexFormat, mesh.indexData, i * 3 + j); + // Check for zero length edges. + for (int j = 0; j < 3; j++) { + const uint32_t edges[6] = { 0, 1, 1, 2, 2, 0 }; + const uint32_t index1 = tri[edges[j * 2 + 0]]; + const uint32_t index2 = tri[edges[j * 2 + 1]]; + const internal::Vector3 pos1 = DecodePosition(mesh, index1); + const internal::Vector3 pos2 = DecodePosition(mesh, index2); + if (EdgeLength(pos1, pos2) <= 0.0f) { + delete heMesh; + error.code = AddMeshErrorCode::ZeroLengthEdge; + error.face = i; + error.index0 = index1; + error.index1 = index2; + return error; + } + } + // Check for zero area faces. + { + const internal::Vector3 a = DecodePosition(mesh, tri[0]); + const internal::Vector3 b = DecodePosition(mesh, tri[1]); + const internal::Vector3 c = DecodePosition(mesh, tri[2]); + const float area = internal::length(internal::cross(b - a, c - a)) * 0.5f; + if (area <= 0.0f) { + delete heMesh; + error.code = AddMeshErrorCode::ZeroAreaFace; + error.face = i; + return error; + } + } + internal::halfedge::Face *face = heMesh->addFace(tri[0], tri[1], tri[2]); + + if (!face && heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::AlreadyAddedEdge) { + //there is still hope for this, no reason to not add, at least add as separate + face = heMesh->addUniqueFace(tri[0], tri[1], tri[2]); + } + + if (!face) { + //continue; + + if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::AlreadyAddedEdge) { + error.code = AddMeshErrorCode::AlreadyAddedEdge; + } else if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::DegenerateColocalEdge) { + error.code = AddMeshErrorCode::DegenerateColocalEdge; + } else if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::DegenerateEdge) { + error.code = AddMeshErrorCode::DegenerateEdge; + } else if (heMesh->errorCode == internal::halfedge::Mesh::ErrorCode::DuplicateEdge) { + error.code = AddMeshErrorCode::DuplicateEdge; + } + error.face = i; + error.index0 = heMesh->errorIndex0; + error.index1 = heMesh->errorIndex1; + delete heMesh; + return error; + } + if (mesh.faceMaterialData) + face->material = mesh.faceMaterialData[i]; + } + heMesh->linkBoundary(); + atlas->heMeshes.push_back(heMesh); + return error; +} + +void Generate(Atlas *atlas, CharterOptions charterOptions, PackerOptions packerOptions) { + xaAssert(atlas); + xaAssert(packerOptions.texelArea > 0); + // Chart meshes. + for (int i = 0; i < (int)atlas->heMeshes.size(); i++) { + std::vector<uint32_t> uncharted_materials; + atlas->atlas.computeCharts(atlas->heMeshes[i], charterOptions, uncharted_materials); + } + atlas->atlas.parameterizeCharts(); + internal::param::AtlasPacker packer(&atlas->atlas); + packer.packCharts(packerOptions); + //float utilization = return packer.computeAtlasUtilization(); + atlas->width = packer.getWidth(); + atlas->height = packer.getHeight(); + // Build output meshes. + atlas->outputMeshes = new OutputMesh *[atlas->heMeshes.size()]; + for (int i = 0; i < (int)atlas->heMeshes.size(); i++) { + const internal::halfedge::Mesh *heMesh = atlas->heMeshes[i]; + OutputMesh *outputMesh = atlas->outputMeshes[i] = new OutputMesh; + const internal::param::MeshCharts *charts = atlas->atlas.meshAt(i); + // Vertices. + outputMesh->vertexCount = charts->vertexCount(); + outputMesh->vertexArray = new OutputVertex[outputMesh->vertexCount]; + for (uint32_t i = 0; i < charts->chartCount(); i++) { + const internal::param::Chart *chart = charts->chartAt(i); + const uint32_t vertexOffset = charts->vertexCountBeforeChartAt(i); + for (uint32_t v = 0; v < chart->vertexCount(); v++) { + OutputVertex &output_vertex = outputMesh->vertexArray[vertexOffset + v]; + output_vertex.xref = chart->mapChartVertexToOriginalVertex(v); + internal::Vector2 uv = chart->chartMesh()->vertexAt(v)->tex; + output_vertex.uv[0] = uv.x; + output_vertex.uv[1] = uv.y; + } + } + // Indices. + outputMesh->indexCount = heMesh->faceCount() * 3; + outputMesh->indexArray = new uint32_t[outputMesh->indexCount]; + for (uint32_t f = 0; f < heMesh->faceCount(); f++) { + const uint32_t c = charts->faceChartAt(f); + const uint32_t i = charts->faceIndexWithinChartAt(f); + const uint32_t vertexOffset = charts->vertexCountBeforeChartAt(c); + const internal::param::Chart *chart = charts->chartAt(c); + xaDebugAssert(i < chart->chartMesh()->faceCount()); + xaDebugAssert(chart->faceAt(i) == f); + const internal::halfedge::Face *face = chart->chartMesh()->faceAt(i); + const internal::halfedge::Edge *edge = face->edge; + outputMesh->indexArray[3 * f + 0] = vertexOffset + edge->vertex->id; + outputMesh->indexArray[3 * f + 1] = vertexOffset + edge->next->vertex->id; + outputMesh->indexArray[3 * f + 2] = vertexOffset + edge->next->next->vertex->id; + } + // Charts. + outputMesh->chartCount = charts->chartCount(); + outputMesh->chartArray = new OutputChart[outputMesh->chartCount]; + for (uint32_t i = 0; i < charts->chartCount(); i++) { + OutputChart *outputChart = &outputMesh->chartArray[i]; + const internal::param::Chart *chart = charts->chartAt(i); + const uint32_t vertexOffset = charts->vertexCountBeforeChartAt(i); + const internal::halfedge::Mesh *mesh = chart->chartMesh(); + outputChart->indexCount = mesh->faceCount() * 3; + outputChart->indexArray = new uint32_t[outputChart->indexCount]; + for (uint32_t j = 0; j < mesh->faceCount(); j++) { + const internal::halfedge::Face *face = mesh->faceAt(j); + const internal::halfedge::Edge *edge = face->edge; + outputChart->indexArray[3 * j + 0] = vertexOffset + edge->vertex->id; + outputChart->indexArray[3 * j + 1] = vertexOffset + edge->next->vertex->id; + outputChart->indexArray[3 * j + 2] = vertexOffset + edge->next->next->vertex->id; + } + } + } +} + +uint32_t GetWidth(const Atlas *atlas) { + xaAssert(atlas); + return atlas->width; +} + +uint32_t GetHeight(const Atlas *atlas) { + xaAssert(atlas); + return atlas->height; +} + +uint32_t GetNumCharts(const Atlas *atlas) { + xaAssert(atlas); + return atlas->atlas.chartCount(); +} + +const OutputMesh *const *GetOutputMeshes(const Atlas *atlas) { + xaAssert(atlas); + return atlas->outputMeshes; +} + +const char *StringForEnum(AddMeshErrorCode::Enum error) { + if (error == AddMeshErrorCode::AlreadyAddedEdge) + return "already added edge"; + if (error == AddMeshErrorCode::DegenerateColocalEdge) + return "degenerate colocal edge"; + if (error == AddMeshErrorCode::DegenerateEdge) + return "degenerate edge"; + if (error == AddMeshErrorCode::DuplicateEdge) + return "duplicate edge"; + if (error == AddMeshErrorCode::IndexOutOfRange) + return "index out of range"; + if (error == AddMeshErrorCode::InvalidIndexCount) + return "invalid index count"; + if (error == AddMeshErrorCode::ZeroAreaFace) + return "zero area face"; + if (error == AddMeshErrorCode::ZeroLengthEdge) + return "zero length edge"; + return "success"; +} + +} // namespace xatlas diff --git a/thirdparty/xatlas/xatlas.h b/thirdparty/xatlas/xatlas.h new file mode 100644 index 0000000000..4140429fee --- /dev/null +++ b/thirdparty/xatlas/xatlas.h @@ -0,0 +1,160 @@ +// This code is in the public domain -- castanyo@yahoo.es +#pragma once +#ifndef XATLAS_H +#define XATLAS_H +#include <float.h> // FLT_MAX +#include <limits.h> +#include <stdint.h> +namespace xatlas { + +typedef void (*PrintFunc)(const char *, ...); + +struct Atlas; + +struct CharterOptions { + float proxyFitMetricWeight; + float roundnessMetricWeight; + float straightnessMetricWeight; + float normalSeamMetricWeight; + float textureSeamMetricWeight; + float maxChartArea; + float maxBoundaryLength; + + CharterOptions() { + // These are the default values we use on The Witness. + proxyFitMetricWeight = 2.0f; + roundnessMetricWeight = 0.01f; + straightnessMetricWeight = 6.0f; + normalSeamMetricWeight = 4.0f; + textureSeamMetricWeight = 0.5f; + /* + proxyFitMetricWeight = 1.0f; + roundnessMetricWeight = 0.1f; + straightnessMetricWeight = 0.25f; + normalSeamMetricWeight = 1.0f; + textureSeamMetricWeight = 0.1f; + */ + maxChartArea = FLT_MAX; + maxBoundaryLength = FLT_MAX; + } +}; + +struct PackMethod { + enum Enum { + TexelArea, // texel_area determines resolution + ApproximateResolution, // guess texel_area to approximately match desired resolution + ExactResolution // run the packer multiple times to exactly match the desired resolution (slow) + }; +}; + +struct PackerOptions { + PackMethod::Enum method; + + // 0 - brute force + // 1 - 4096 attempts + // 2 - 2048 + // 3 - 1024 + // 4 - 512 + // other - 256 + // Avoid brute force packing, since it can be unusably slow in some situations. + int quality; + + float texelArea; // This is not really texel area, but 1 / texel width? + uint32_t resolution; + bool blockAlign; // Align charts to 4x4 blocks. + bool conservative; // Pack charts with extra padding. + int padding; + + PackerOptions() { + method = PackMethod::ApproximateResolution; + quality = 1; + texelArea = 8; + resolution = 512; + blockAlign = false; + conservative = false; + padding = 0; + } +}; + +struct AddMeshErrorCode { + enum Enum { + Success, + AlreadyAddedEdge, // index0 and index1 are the edge indices + DegenerateColocalEdge, // index0 and index1 are the edge indices + DegenerateEdge, // index0 and index1 are the edge indices + DuplicateEdge, // index0 and index1 are the edge indices + IndexOutOfRange, // index0 is the index + InvalidIndexCount, // not evenly divisible by 3 - expecting triangles + ZeroAreaFace, + ZeroLengthEdge // index0 and index1 are the edge indices + }; +}; + +struct AddMeshError { + AddMeshErrorCode::Enum code; + uint32_t face; + uint32_t index0, index1; +}; + +struct IndexFormat { + enum Enum { + HalfFloat, + Float + }; +}; + +struct InputMesh { + uint32_t vertexCount; + const void *vertexPositionData; + uint32_t vertexPositionStride; + const void *vertexNormalData; // optional + uint32_t vertexNormalStride; // optional + + // optional + // The input UVs are provided as a hint to the chart generator. + const void *vertexUvData; + uint32_t vertexUvStride; + + uint32_t indexCount; + const void *indexData; + IndexFormat::Enum indexFormat; + + // optional. indexCount / 3 in length. + // Charter also uses material boundaries as a hint to cut charts. + const uint16_t *faceMaterialData; +}; + +struct OutputChart { + uint32_t *indexArray; + uint32_t indexCount; +}; + +struct OutputVertex { + float uv[2]; + uint32_t xref; // Index of input vertex from which this output vertex originated. +}; + +struct OutputMesh { + OutputChart *chartArray; + uint32_t chartCount; + uint32_t *indexArray; + uint32_t indexCount; + OutputVertex *vertexArray; + uint32_t vertexCount; +}; + +void SetPrint(PrintFunc print); +Atlas *Create(); +void Destroy(Atlas *atlas); +// useColocalVertices - generates fewer charts (good), but is more sensitive to bad geometry. +AddMeshError AddMesh(Atlas *atlas, const InputMesh &mesh, bool useColocalVertices = true); +void Generate(Atlas *atlas, CharterOptions charterOptions = CharterOptions(), PackerOptions packerOptions = PackerOptions()); +uint32_t GetWidth(const Atlas *atlas); +uint32_t GetHeight(const Atlas *atlas); +uint32_t GetNumCharts(const Atlas *atlas); +const OutputMesh *const *GetOutputMeshes(const Atlas *atlas); +const char *StringForEnum(AddMeshErrorCode::Enum error); + +} // namespace xatlas + +#endif // XATLAS_H diff --git a/thirdparty/zstd/SCsub b/thirdparty/zstd/SCsub deleted file mode 100644 index 899a18e1cf..0000000000 --- a/thirdparty/zstd/SCsub +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python - -Import('env') - -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.add_source_files(env.core_sources, thirdparty_zstd_sources) -env.Append(CPPPATH=["#thirdparty/zstd", "#thirdparty/zstd/common"]) -env.Append(CCFLAGS="-DZSTD_STATIC_LINKING_ONLY") |