diff options
319 files changed, 10995 insertions, 2063 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..85f76000b7 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 @@ -190,6 +191,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)) @@ -333,12 +335,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']) 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/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..d33d436b74 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -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) { diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp index 5503b8d59c..17b77bc535 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -38,20 +38,23 @@ _FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_mas switch (mode) { case MultiplayerAPI::RPC_MODE_DISABLED: { - //do nothing + // Do nothing. } break; case MultiplayerAPI::RPC_MODE_REMOTE: { - //do nothing also, no need to call local + // Do nothing also. Remote cannot produce a local call. } break; + case MultiplayerAPI::RPC_MODE_MASTERSYNC: { + if (is_master) + r_skip_rpc = true; // I am the master, so skip remote call. + } // Do not break, fall over to other sync. case MultiplayerAPI::RPC_MODE_REMOTESYNC: - case MultiplayerAPI::RPC_MODE_MASTERSYNC: case MultiplayerAPI::RPC_MODE_PUPPETSYNC: { - //call it, sync always results in call + // Call it, sync always results in a local call. return true; } break; case MultiplayerAPI::RPC_MODE_MASTER: { if (is_master) - r_skip_rpc = true; //no other master so.. + r_skip_rpc = true; // I am the master, so skip remote call. return is_master; } break; case MultiplayerAPI::RPC_MODE_PUPPET: { @@ -91,7 +94,7 @@ void MultiplayerAPI::poll() { network_peer->poll(); - if (!network_peer.is_valid()) //it's possible that polling might have resulted in a disconnection, so check here + if (!network_peer.is_valid()) // It's possible that polling might have resulted in a disconnection, so check here. return; while (network_peer->get_available_packet_count()) { @@ -110,7 +113,7 @@ void MultiplayerAPI::poll() { rpc_sender_id = 0; if (!network_peer.is_valid()) { - break; //it's also possible that a packet or RPC caused a disconnection, so also check here + break; // It's also possible that a packet or RPC caused a disconnection, so also check here. } } } @@ -157,7 +160,9 @@ Ref<NetworkedMultiplayerPeer> MultiplayerAPI::get_network_peer() const { void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) { + ERR_EXPLAIN("Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it"); ERR_FAIL_COND(root_node == NULL); + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_packet_len < 1); uint8_t packet_type = p_packet[0]; @@ -177,13 +182,15 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ case NETWORK_COMMAND_REMOTE_CALL: case NETWORK_COMMAND_REMOTE_SET: { + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_packet_len < 6); Node *node = _process_get_node(p_from, p_packet, p_packet_len); + ERR_EXPLAIN("Invalid packet received. Requested node was not found."); ERR_FAIL_COND(node == NULL); - //detect cstring end + // Detect cstring end. int len_end = 5; for (; len_end < p_packet_len; len_end++) { if (p_packet[len_end] == 0) { @@ -191,6 +198,7 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ } } + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(len_end >= p_packet_len); StringName name = String::utf8((const char *)&p_packet[5]); @@ -219,9 +227,11 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int Node *node = NULL; if (target & 0x80000000) { - //use full path (not cached yet) + // Use full path (not cached yet). int ofs = target & 0x7FFFFFFF; + + ERR_EXPLAIN("Invalid packet received. Size smaller than declared."); ERR_FAIL_COND_V(ofs >= p_packet_len, NULL); String paths; @@ -234,17 +244,19 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int if (!node) ERR_PRINTS("Failed to get path from RPC: " + String(np)); } else { - //use cached path + // Use cached path. int id = target; Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from); + ERR_EXPLAIN("Invalid packet received. Requests invalid peer cache."); ERR_FAIL_COND_V(!E, NULL); Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(id); + ERR_EXPLAIN("Invalid packet received. Unabled to find requested cached node."); ERR_FAIL_COND_V(!F, NULL); PathGetCache::NodeInfo *ni = &F->get(); - //do proper caching later + // Do proper caching later. node = root_node->get_node(ni->path); if (!node) @@ -255,9 +267,10 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_offset >= p_packet_len); - // Check that remote can call the RPC on this node + // Check that remote can call the RPC on this node. RPCMode rpc_mode = RPC_MODE_DISABLED; const Map<StringName, RPCMode>::Element *E = p_node->get_node_rpc_mode(p_name); if (E) { @@ -265,6 +278,8 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ } else if (p_node->get_script_instance()) { rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_name); } + + ERR_EXPLAIN("RPC '" + String(p_name) + "' is not allowed from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + "."); ERR_FAIL_COND(!_can_call_mode(p_node, rpc_mode, p_from)); int argc = p_packet[p_offset]; @@ -277,11 +292,14 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ for (int i = 0; i < argc; i++) { + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_offset >= p_packet_len); + int vlen; Error err = decode_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen); + ERR_EXPLAIN("Invalid packet received. Unable to decode RPC argument."); ERR_FAIL_COND(err != OK); - //args[i]=p_packet[3+i]; + argp.write[i] = &args[i]; p_offset += vlen; } @@ -298,9 +316,10 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_offset >= p_packet_len); - // Check that remote can call the RSET on this node + // Check that remote can call the RSET on this node. RPCMode rset_mode = RPC_MODE_DISABLED; const Map<StringName, RPCMode>::Element *E = p_node->get_node_rset_mode(p_name); if (E) { @@ -308,10 +327,15 @@ void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p } else if (p_node->get_script_instance()) { rset_mode = p_node->get_script_instance()->get_rset_mode(p_name); } + + ERR_EXPLAIN("RSET '" + String(p_name) + "' is not allowed from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + "."); ERR_FAIL_COND(!_can_call_mode(p_node, rset_mode, p_from)); Variant value; - decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset); + Error err = decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset); + + ERR_EXPLAIN("Invalid packet received. Unable to decode RSET value."); + ERR_FAIL_COND(err != OK); bool valid; @@ -324,6 +348,7 @@ void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) { + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_packet_len < 5); int id = decode_uint32(&p_packet[1]); @@ -342,9 +367,7 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, path_get_cache[p_from].nodes[id] = ni; - //send ack - - //encode path + // Encode path to send ack. CharString pname = String(path).utf8(); int len = encode_cstring(pname.get_data(), NULL); @@ -361,6 +384,7 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) { + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_packet_len < 2); String paths; @@ -369,31 +393,33 @@ void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, NodePath path = paths; PathSentCache *psc = path_send_cache.getptr(path); + ERR_EXPLAIN("Invalid packet received. Tries to confirm a path which was not found in cache."); ERR_FAIL_COND(!psc); Map<int, bool>::Element *E = psc->confirmed_peers.find(p_from); + ERR_EXPLAIN("Invalid packet received. Source peer was not found in cache for the given path."); ERR_FAIL_COND(!E); E->get() = true; } bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int p_target) { bool has_all_peers = true; - List<int> peers_to_add; //if one is missing, take note to add it + List<int> peers_to_add; // If one is missing, take note to add it. for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { if (p_target < 0 && E->get() == -p_target) - continue; //continue, excluded + continue; // Continue, excluded. if (p_target > 0 && E->get() != p_target) - continue; //continue, not for this peer + continue; // Continue, not for this peer. Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); if (!F || F->get() == false) { - //path was not cached, or was cached but is unconfirmed + // Path was not cached, or was cached but is unconfirmed. if (!F) { - //not cached at all, take note + // Not cached at all, take note. peers_to_add.push_back(E->get()); } @@ -401,11 +427,11 @@ bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int } } - //those that need to be added, send a message for this + // Those that need to be added, send a message for this. for (List<int>::Element *E = peers_to_add.front(); E; E = E->next()) { - //encode function name + // Encode function name. CharString pname = String(p_path).utf8(); int len = encode_cstring(pname.get_data(), NULL); @@ -416,11 +442,11 @@ bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int encode_uint32(psc->id, &packet.write[1]); encode_cstring(pname.get_data(), &packet.write[5]); - network_peer->set_target_peer(E->get()); //to all of you + network_peer->set_target_peer(E->get()); // To all of you. network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); network_peer->put_packet(packet.ptr(), packet.size()); - psc->confirmed_peers.insert(E->get(), false); //insert into confirmed, but as false since it was not confirmed + psc->confirmed_peers.insert(E->get(), false); // Insert into confirmed, but as false since it was not confirmed. } return has_all_peers; @@ -459,35 +485,36 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p } NodePath from_path = (root_node->get_path()).rel_path_to(p_from->get_path()); + ERR_EXPLAIN("Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!"); ERR_FAIL_COND(from_path.is_empty()); - //see if the path is cached + // See if the path is cached. PathSentCache *psc = path_send_cache.getptr(from_path); if (!psc) { - //path is not cached, create + // Path is not cached, create. path_send_cache[from_path] = PathSentCache(); psc = path_send_cache.getptr(from_path); psc->id = last_send_cache_id++; } - //create base packet, lots of hardcode because it must be tight + // Create base packet, lots of hardcode because it must be tight. int ofs = 0; #define MAKE_ROOM(m_amount) \ if (packet_cache.size() < m_amount) packet_cache.resize(m_amount); - //encode type + // Encode type. MAKE_ROOM(1); packet_cache.write[0] = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL; ofs += 1; - //encode ID + // Encode ID. MAKE_ROOM(ofs + 4); encode_uint32(psc->id, &(packet_cache.write[ofs])); ofs += 4; - //encode function name + // Encode function name. CharString name = String(p_name).utf8(); int len = encode_cstring(name.get_data(), NULL); MAKE_ROOM(ofs + len); @@ -495,20 +522,22 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p ofs += len; if (p_set) { - //set argument + // Set argument. Error err = encode_variant(*p_arg[0], NULL, len); + ERR_EXPLAIN("Unable to encode RSET value. THIS IS LIKELY A BUG IN THE ENGINE!"); ERR_FAIL_COND(err != OK); MAKE_ROOM(ofs + len); encode_variant(*p_arg[0], &(packet_cache.write[ofs]), len); ofs += len; } else { - //call arguments + // Call arguments. MAKE_ROOM(ofs + 1); packet_cache.write[ofs] = p_argcount; ofs += 1; for (int i = 0; i < p_argcount; i++) { Error err = encode_variant(*p_arg[i], NULL, len); + ERR_EXPLAIN("Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!"); ERR_FAIL_COND(err != OK); MAKE_ROOM(ofs + len); encode_variant(*p_arg[i], &(packet_cache.write[ofs]), len); @@ -516,21 +545,21 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p } } - //see if all peers have cached path (is so, call can be fast) + // See if all peers have cached path (is so, call can be fast). bool has_all_peers = _send_confirm_path(from_path, psc, p_to); - //take chance and set transfer mode, since all send methods will use it + // Take chance and set transfer mode, since all send methods will use it. network_peer->set_transfer_mode(p_unreliable ? NetworkedMultiplayerPeer::TRANSFER_MODE_UNRELIABLE : NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); if (has_all_peers) { - //they all have verified paths, so send fast - network_peer->set_target_peer(p_to); //to all of you - network_peer->put_packet(packet_cache.ptr(), ofs); //a message with love + // They all have verified paths, so send fast. + network_peer->set_target_peer(p_to); // To all of you. + network_peer->put_packet(packet_cache.ptr(), ofs); // A message with love. } else { - //not all verified path, so send one by one + // Not all verified path, so send one by one. - //apend path at the end, since we will need it for some packets + // Append path at the end, since we will need it for some packets. CharString pname = String(from_path).utf8(); int path_len = encode_cstring(pname.get_data(), NULL); MAKE_ROOM(ofs + path_len); @@ -539,23 +568,23 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { if (p_to < 0 && E->get() == -p_to) - continue; //continue, excluded + continue; // Continue, excluded. if (p_to > 0 && E->get() != p_to) - continue; //continue, not for this peer + continue; // Continue, not for this peer. Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); - ERR_CONTINUE(!F); //should never happen + ERR_CONTINUE(!F); // Should never happen. - network_peer->set_target_peer(E->get()); //to this one specifically + network_peer->set_target_peer(E->get()); // To this one specifically. if (F->get() == true) { - //this one confirmed path, so use id + // This one confirmed path, so use id. encode_uint32(psc->id, &(packet_cache.write[1])); network_peer->put_packet(packet_cache.ptr(), ofs); } else { - //this one did not confirm path yet, so use entire path (sorry!) - encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); //offset to path and flag + // This one did not confirm path yet, so use entire path (sorry!). + encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); // Offset to path and flag. network_peer->put_packet(packet_cache.ptr(), ofs + path_len); } } @@ -570,7 +599,7 @@ void MultiplayerAPI::_add_peer(int p_id) { void MultiplayerAPI::_del_peer(int p_id) { connected_peers.erase(p_id); - path_get_cache.erase(p_id); //I no longer need your cache, sorry + path_get_cache.erase(p_id); // I no longer need your cache, sorry. emit_signal("network_peer_disconnected", p_id); } @@ -591,8 +620,12 @@ void MultiplayerAPI::_server_disconnected() { void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) { - ERR_FAIL_COND(!p_node->is_inside_tree()); + ERR_EXPLAIN("Trying to call an RPC while no network peer is active."); ERR_FAIL_COND(!network_peer.is_valid()); + ERR_EXPLAIN("Trying to call an RPC on a node which is not inside SceneTree."); + ERR_FAIL_COND(!p_node->is_inside_tree()); + ERR_EXPLAIN("Trying to call an RPC via a network peer which is not connected."); + ERR_FAIL_COND(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED); int node_id = network_peer->get_unique_id(); bool skip_rpc = false; @@ -601,7 +634,7 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const bool is_master = p_node->is_network_master(); if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { - //check that send mode can use local call + // Check that send mode can use local call. const Map<StringName, RPCMode>::Element *E = p_node->get_node_rpc_mode(p_method); if (E) { @@ -609,9 +642,9 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const } if (call_local_native) { - // done below + // Done below. } else if (p_node->get_script_instance()) { - //attempt with script + // Attempt with script. RPCMode rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method); call_local_script = _should_call_local(rpc_mode, is_master, skip_rpc); } @@ -647,15 +680,19 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value) { - ERR_FAIL_COND(!p_node->is_inside_tree()); + ERR_EXPLAIN("Trying to RSET while no network peer is active."); ERR_FAIL_COND(!network_peer.is_valid()); + ERR_EXPLAIN("Trying to RSET on a node which is not inside SceneTree."); + ERR_FAIL_COND(!p_node->is_inside_tree()); + ERR_EXPLAIN("Trying to send an RSET via a network peer which is not connected."); + ERR_FAIL_COND(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED); int node_id = network_peer->get_unique_id(); bool is_master = p_node->is_network_master(); bool skip_rset = false; if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { - //check that send mode can use local call + // Check that send mode can use local call. bool set_local = false; @@ -675,7 +712,7 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const return; } } else if (p_node->get_script_instance()) { - //attempt with script + // Attempt with script. RPCMode rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property); set_local = _should_call_local(rpc_mode, is_master, skip_rset); @@ -703,8 +740,11 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const Error MultiplayerAPI::send_bytes(PoolVector<uint8_t> p_data, int p_to, NetworkedMultiplayerPeer::TransferMode p_mode) { + ERR_EXPLAIN("Trying to send an empty raw packet."); ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA); + ERR_EXPLAIN("Trying to send a raw packet while no network peer is active."); ERR_FAIL_COND_V(!network_peer.is_valid(), ERR_UNCONFIGURED); + ERR_EXPLAIN("Trying to send a raw packet via a network peer which is not connected."); ERR_FAIL_COND_V(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED); MAKE_ROOM(p_data.size() + 1); @@ -720,6 +760,7 @@ Error MultiplayerAPI::send_bytes(PoolVector<uint8_t> p_data, int p_to, Networked void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) { + ERR_EXPLAIN("Invalid packet received. Size too small."); ERR_FAIL_COND(p_packet_len < 2); PoolVector<uint8_t> out; @@ -734,30 +775,36 @@ void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_pac int MultiplayerAPI::get_network_unique_id() const { + ERR_EXPLAIN("No network peer is assigned. Unable to get unique network ID."); ERR_FAIL_COND_V(!network_peer.is_valid(), 0); return network_peer->get_unique_id(); } bool MultiplayerAPI::is_network_server() const { + // XXX Maybe fail silently? Maybe should actually return true to make development of both local and online multiplayer easier? + ERR_EXPLAIN("No network peer is assigned. I can't be a server."); ERR_FAIL_COND_V(!network_peer.is_valid(), false); return network_peer->is_server(); } void MultiplayerAPI::set_refuse_new_network_connections(bool p_refuse) { + ERR_EXPLAIN("No network peer is assigned. Unable to set 'refuse_new_connections'."); ERR_FAIL_COND(!network_peer.is_valid()); network_peer->set_refuse_new_connections(p_refuse); } bool MultiplayerAPI::is_refusing_new_network_connections() const { + ERR_EXPLAIN("No network peer is assigned. Unable to get 'refuse_new_connections'."); ERR_FAIL_COND_V(!network_peer.is_valid(), false); return network_peer->is_refusing_new_connections(); } Vector<int> MultiplayerAPI::get_network_connected_peers() const { + ERR_EXPLAIN("No network peer is assigned. Assume no peers are connected."); ERR_FAIL_COND_V(!network_peer.is_valid(), Vector<int>()); Vector<int> ret; @@ -802,9 +849,9 @@ void MultiplayerAPI::_bind_methods() { BIND_ENUM_CONSTANT(RPC_MODE_REMOTE); BIND_ENUM_CONSTANT(RPC_MODE_MASTER); BIND_ENUM_CONSTANT(RPC_MODE_PUPPET); - BIND_ENUM_CONSTANT(RPC_MODE_SLAVE); // deprecated + BIND_ENUM_CONSTANT(RPC_MODE_SLAVE); // Deprecated. BIND_ENUM_CONSTANT(RPC_MODE_REMOTESYNC); - BIND_ENUM_CONSTANT(RPC_MODE_SYNC); // deprecated + BIND_ENUM_CONSTANT(RPC_MODE_SYNC); // Deprecated. BIND_ENUM_CONSTANT(RPC_MODE_MASTERSYNC); BIND_ENUM_CONSTANT(RPC_MODE_PUPPETSYNC); } 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/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/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..345c018e6d 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) { } @@ -2014,11 +2014,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 +2097,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..c883d44f36 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); } 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/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/Array.xml b/doc/classes/Array.xml index 3bd621799a..b134650f8d 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -321,7 +321,7 @@ static func sort(a, b): if a[0] < b[0]: return true - return false + return false var my_items = [[5, "Potato"], [9, "Rice"], [4, "Tomato"]] my_items.sort_custom(MyCustomSorter, "sort") 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/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/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/OS.xml b/doc/classes/OS.xml index c41084f853..dad4ce898d 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -101,7 +101,7 @@ </argument> <description> Execute the file at the given path with the arguments passed as an array of strings. Platform path resolution will take place. The resolved file must exist and be executable. - The arguments are used in the given order and separated by a space, so [code]OS.execute('ping', ['-c', '3', 'godotengine.org'])[/code] will resolve to [code]ping -c 3 godotengine.org[/code] in the system's shell. + The arguments are used in the given order and separated by a space, so [code]OS.execute('ping', ['-w', '3', 'godotengine.org'], false)[/code] will resolve to [code]ping -w 3 godotengine.org[/code] in the system's shell. This method has slightly different behaviour based on whether the [code]blocking[/code] mode is enabled. When [code]blocking[/code] is enabled, the Godot thread will pause its execution while waiting for the process to terminate. The shell output of the process will be written to the [code]output[/code] array as a single string. When the process terminates, the Godot thread will resume execution. When [code]blocking[/code] is disabled, the Godot thread will continue while the new process runs. It is not possible to retrieve the shell output in non-blocking mode, so [code]output[/code] will be empty. 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/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.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 b3ce873b65..043a5047ca 100644 --- a/drivers/gles2/rasterizer_storage_gles2.cpp +++ b/drivers/gles2/rasterizer_storage_gles2.cpp @@ -356,7 +356,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 +496,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 +575,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 +948,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 +1294,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 +2625,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 +3100,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 +3292,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 +3580,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 +3765,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 +3915,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, GL_DEPTH_COMPONENT, rt->width, rt->height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rt->depth); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -3862,6 +4131,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 +4312,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 +4382,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 +4493,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,17 +4511,20 @@ 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); } #ifdef GLES_OVER_GL + //this needs to be enabled manually in OpenGL 2.1 + glEnable(_EXT_TEXTURE_CUBE_MAP_SEAMLESS); + glEnable(GL_POINT_SPRITE); + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); #endif config.force_vertex_shading = GLOBAL_GET("rendering/quality/shading/force_vertex_shading"); @@ -4247,6 +4542,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..cc3bae060f 100644 --- a/drivers/gles2/shaders/scene.glsl +++ b/drivers/gles2/shaders/scene.glsl @@ -27,15 +27,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 +43,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 +102,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 +130,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 +165,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 +257,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 +300,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 +312,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 +462,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) { - vec3 attenuation = vec3(omni_attenuation); - light_att = vec3(omni_attenuation); + float omni_attenuation = pow(1.0 - normalized_distance, light_attenuation); + + vec3 attenuation = vec3(omni_attenuation); + light_att = vec3(omni_attenuation); + } else { + light_att = vec3(0.0); + } L = normalize(light_vec); @@ -453,17 +482,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_cutoff = light_spot_angle; + float spot_attenuation = pow(1.0 - normalized_distance, light_attenuation); + vec3 spot_dir = light_direction; - 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 spot_cutoff = light_spot_angle; - spot_attenuation *= 1.0 - pow(spot_rim, light_spot_attenuation); + float angle = dot(-normalize(light_rel_vec), spot_dir); + + 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 +534,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 +622,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 +633,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 +826,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 +883,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 @@ -1034,6 +1277,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 +1301,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 @@ -1091,7 +1335,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 +1367,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,10 +1479,14 @@ 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 @@ -1311,17 +1652,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 +1743,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 +1759,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/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.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..1fcd4e02ac 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -1282,6 +1282,8 @@ bool RasterizerSceneGLES3::_setup_material(RasterizerStorageGLES3::Material *p_m case ShaderLanguage::TYPE_SAMPLER2DARRAY: { // TODO } break; + + default: {} } } @@ -1509,6 +1511,7 @@ void RasterizerSceneGLES3::_setup_geometry(RenderList::Element *e, const Transfo } } break; + default: {} } } @@ -1830,6 +1833,7 @@ void RasterizerSceneGLES3::_render_geometry(RenderList::Element *e) { } } break; + default: {} } } @@ -3036,13 +3040,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 +3214,7 @@ void RasterizerSceneGLES3::_fill_render_list(InstanceBase **p_cull_result, int p } } break; + default: {} } } } @@ -4295,7 +4301,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 +4338,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); break; + default: {} } } @@ -4501,7 +4507,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 +4589,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 +4598,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..b31c5ff7bb 100644 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ b/drivers/gles3/rasterizer_storage_gles3.h @@ -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..986c36c67c 100644 --- a/drivers/png/SCsub +++ b/drivers/png/SCsub @@ -26,14 +26,22 @@ 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]) # 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 +49,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/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..d5ac8c7008 100644 --- a/editor/SCsub +++ b/editor/SCsub @@ -1,6 +1,7 @@ #!/usr/bin/env python Import('env') + env.editor_sources = [] import os @@ -89,5 +90,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 ac28fb9b99..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(); @@ -4923,8 +4921,8 @@ AnimationTrackEditor::AnimationTrackEditor() { //this shortcut will be checked from the track itself. so no need to enable it here (will conflict with scenetree dock) edit->get_popup()->add_separator(); - edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/goto_next_step", TTR("Goto Next Step"), KEY_MASK_CMD | KEY_RIGHT), EDIT_GOTO_NEXT_STEP); - edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/goto_prev_step", TTR("Goto Prev Step"), KEY_MASK_CMD | KEY_LEFT), EDIT_GOTO_PREV_STEP); + edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/goto_next_step", TTR("Go to Next Step"), KEY_MASK_CMD | KEY_RIGHT), EDIT_GOTO_NEXT_STEP); + edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/goto_prev_step", TTR("Go to Previous Step"), KEY_MASK_CMD | KEY_LEFT), EDIT_GOTO_PREV_STEP); edit->get_popup()->add_separator(); edit->get_popup()->add_item(TTR("Optimize Animation"), EDIT_OPTIMIZE_ANIMATION); edit->get_popup()->add_item(TTR("Clean-Up Animation"), EDIT_CLEAN_UP_ANIMATION); 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_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..c9871bf27d 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" @@ -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() { @@ -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..690fbd2c93 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); } @@ -1759,7 +1761,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() { @@ -2663,7 +2665,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 +2673,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 +2689,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..4e638cb4ac 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; diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 7ed7b920d9..39d2f685b6 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; @@ -945,6 +932,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // TooltipPanel Ref<StyleBoxFlat> style_tooltip = style_popup->duplicate(); + float v = MAX(border_size * EDSCALE, 1.0); + style_tooltip->set_default_margin(MARGIN_LEFT, v); + style_tooltip->set_default_margin(MARGIN_TOP, v); + style_tooltip->set_default_margin(MARGIN_RIGHT, v); + style_tooltip->set_default_margin(MARGIN_BOTTOM, v); style_tooltip->set_bg_color(Color(mono_color.r, mono_color.g, mono_color.b, 0.9)); style_tooltip->set_border_width_all(border_width); style_tooltip->set_border_color_all(mono_color); @@ -1064,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"); @@ -1099,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..42fd38b654 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -255,7 +255,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 +267,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 +384,7 @@ void FileSystemDock::_notification(int p_what) { } if (should_update_files) { - _update_files(true); + _update_file_list(true); } // Change full tree mode @@ -415,7 +415,7 @@ void FileSystemDock::_tree_multi_selected(Object *p_item, int p_column, bool p_s // Update the file list if (!updating_tree && display_mode == DISPLAY_MODE_SPLIT) { - _update_files(false); + _update_file_list(false); } } @@ -453,7 +453,7 @@ void FileSystemDock::navigate_to_path(const String &p_path) { if (display_mode == DISPLAY_MODE_SPLIT) { _update_tree(_compute_uncollapsed_paths()); - _update_files(false); + _update_file_list(false); } else if (display_mode == DISPLAY_MODE_TREE_ONLY) { if (path.ends_with("/")) { _go_to_file_list(); @@ -461,7 +461,7 @@ void FileSystemDock::navigate_to_path(const String &p_path) { _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 +482,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); + } + } } } @@ -527,7 +533,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 +564,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; @@ -598,7 +604,7 @@ void FileSystemDock::_update_files(bool p_keep_selection) { 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 +622,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); @@ -624,6 +631,7 @@ void FileSystemDock::_update_files(bool p_keep_selection) { } if (use_folders) { + // Display folders in the list Ref<Texture> folderIcon = (use_thumbnails) ? folder_thumbnail : get_icon("folder", "FileDialog"); if (directory != "res://") { @@ -651,13 +659,13 @@ void FileSystemDock::_update_files(bool p_keep_selection) { } List<FileInfo> filelist; - if (searched_string.length() > 0) { - + // Display the search results _search(EditorFileSystem::get_singleton()->get_filesystem(), &filelist, 128); filelist.sort(); } else { + // Display the folder content for (int i = 0; i < efd->get_file_count(); i++) { FileInfo fi; @@ -673,7 +681,6 @@ void FileSystemDock::_update_files(bool p_keep_selection) { } String oi = "Object"; - for (List<FileInfo>::Element *E = filelist.front(); E; E = E->next()) { FileInfo *finfo = &(E->get()); String fname = finfo->name; @@ -685,6 +692,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 +702,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 +734,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]; @@ -766,7 +780,7 @@ void FileSystemDock::_go_to_file_list() { } else { bool collapsed = tree->get_selected()->is_collapsed(); tree->get_selected()->set_collapsed(!collapsed); - _update_files(false); + _update_file_list(false); } } @@ -809,7 +823,7 @@ void FileSystemDock::_fs_changed() { } if (file_list_vb->is_visible()) { - _update_files(true); + _update_file_list(true); } set_process(false); @@ -855,7 +869,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); @@ -1592,13 +1606,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; } @@ -1859,7 +1873,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; } diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 51c8791b25..5208b2c667 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(); diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index 11d0b67bd6..2fdf1c82c0 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -302,7 +302,7 @@ FindInFilesDialog::FindInFilesDialog() { set_custom_minimum_size(Size2(400, 190)); set_resizable(true); - set_title(TTR("Find in files")); + set_title(TTR("Find in Files")); VBoxContainer *vbc = memnew(VBoxContainer); vbc->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_BEGIN, 8 * EDSCALE); 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/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 72451cfcb7..f65c8cbd0d 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -1654,6 +1654,8 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) { Transform2D unscaled_transform = (xform * canvas_item->get_transform().affine_inverse() * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position())).orthonormalized(); Transform2D simple_xform = viewport->get_transform() * unscaled_transform; + drag_type = DRAG_SCALE_BOTH; + Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE); Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); if (x_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) { @@ -1663,18 +1665,17 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) { if (y_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) { drag_type = DRAG_SCALE_Y; } - if (drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y) { - drag_from = transform.affine_inverse().xform(b->get_position()); - drag_selection = List<CanvasItem *>(); - drag_selection.push_back(canvas_item); - _save_canvas_item_state(drag_selection); - return true; - } + + drag_from = transform.affine_inverse().xform(b->get_position()); + drag_selection = List<CanvasItem *>(); + drag_selection.push_back(canvas_item); + _save_canvas_item_state(drag_selection); + return true; } } } - if (drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y) { + if (drag_type == DRAG_SCALE_BOTH || drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y) { // Resize the node if (m.is_valid()) { _restore_canvas_item_state(drag_selection); @@ -1682,24 +1683,49 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) { drag_to = transform.affine_inverse().xform(m->get_position()); + Transform2D parent_xform = canvas_item->get_global_transform_with_canvas() * canvas_item->get_transform().affine_inverse(); + Transform2D unscaled_transform = (transform * parent_xform * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position())).orthonormalized(); + Transform2D simple_xform = (viewport->get_transform() * unscaled_transform).affine_inverse() * transform; + bool uniform = m->get_shift(); - Point2 offset = drag_to - drag_from; + + Point2 drag_from_local = simple_xform.xform(drag_from); + Point2 drag_to_local = simple_xform.xform(drag_to); + Point2 offset = drag_to_local - drag_from_local; + Size2 scale = canvas_item->call("get_scale"); float ratio = scale.y / scale.x; - if (drag_type == DRAG_SCALE_X) { - scale.x += offset.x / SCALE_HANDLE_DISTANCE; + if (drag_type == DRAG_SCALE_BOTH) { + Size2 scale_factor = drag_to_local / drag_from_local; if (uniform) { - scale.y = scale.x * ratio; + if (ABS(offset.x) > ABS(offset.y)) { + scale.x *= scale_factor.x; + scale.y = scale.x * ratio; + } else { + scale.y *= scale_factor.y; + scale.x = scale.y / ratio; + } + } else { + scale *= scale_factor; } - canvas_item->call("set_scale", scale); - - } else if (drag_type == DRAG_SCALE_Y) { - scale.y -= offset.y / SCALE_HANDLE_DISTANCE; - if (uniform) { - scale.x = scale.y / ratio; + } else { + Size2 scale_factor = Vector2(offset.x, -offset.y) / SCALE_HANDLE_DISTANCE; + Size2 parent_scale = parent_xform.get_scale(); + scale_factor *= Vector2(1.0 / parent_scale.x, 1.0 / parent_scale.y); + if (drag_type == DRAG_SCALE_X) { + scale.x += scale_factor.x; + if (uniform) { + scale.y = scale.x * ratio; + } + } else if (drag_type == DRAG_SCALE_Y) { + scale.y += scale_factor.y; + if (uniform) { + scale.x = scale.y / ratio; + } } - canvas_item->call("set_scale", scale); } + canvas_item->call("set_scale", scale); + return true; } // Confirm resize @@ -2352,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); } } } @@ -2364,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/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 2d539d6727..c788a63d56 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -195,6 +195,7 @@ private: DRAG_MOVE, DRAG_SCALE_X, DRAG_SCALE_Y, + DRAG_SCALE_BOTH, DRAG_ROTATE, DRAG_PIVOT, DRAG_V_GUIDE, 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/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 c8e7bfb74b..5e000ca6ef 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -3013,7 +3013,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/show_in_file_system", TTR("Show In File System")), SHOW_IN_FILE_SYSTEM); file_menu->get_popup()->add_separator(); - file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_previous", TTR("History Prev"), KEY_MASK_ALT | KEY_LEFT), WINDOW_PREV); + file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_previous", TTR("History Previous"), KEY_MASK_ALT | KEY_LEFT), WINDOW_PREV); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_next", TTR("History Next"), KEY_MASK_ALT | KEY_RIGHT), WINDOW_NEXT); file_menu->get_popup()->add_separator(); @@ -3060,7 +3060,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { debug_menu->get_popup()->add_separator(); //debug_menu->get_popup()->add_check_item("Show Debugger",DEBUG_SHOW); debug_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("debugger/keep_debugger_open", TTR("Keep Debugger Open")), DEBUG_SHOW_KEEP_OPEN); - debug_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("debugger/debug_with_exteral_editor", TTR("Debug with external editor")), DEBUG_WITH_EXTERNAL_EDITOR); + debug_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("debugger/debug_with_external_editor", TTR("Debug with External Editor")), DEBUG_WITH_EXTERNAL_EDITOR); debug_menu->get_popup()->connect("id_pressed", this, "_menu_option"); debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index d4ddaf274f..23babdf07b 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -941,6 +941,7 @@ void ScriptTextEditor::_edit_option(int p_op) { case SEARCH_LOCATE_FUNCTION: { quick_open->popup(get_functions()); + quick_open->set_title(TTR("Go to Function")); } break; case SEARCH_GOTO_LINE: { @@ -1275,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; @@ -1567,8 +1567,8 @@ void ScriptTextEditor::register_editor() { ED_SHORTCUT("script_text_editor/toggle_breakpoint", TTR("Toggle Breakpoint"), KEY_F9); #endif ED_SHORTCUT("script_text_editor/remove_all_breakpoints", TTR("Remove All Breakpoints"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F9); - ED_SHORTCUT("script_text_editor/goto_next_breakpoint", TTR("Goto Next Breakpoint"), KEY_MASK_CMD | KEY_PERIOD); - ED_SHORTCUT("script_text_editor/goto_previous_breakpoint", TTR("Goto Previous Breakpoint"), KEY_MASK_CMD | KEY_COMMA); + ED_SHORTCUT("script_text_editor/goto_next_breakpoint", TTR("Go to Next Breakpoint"), KEY_MASK_CMD | KEY_PERIOD); + ED_SHORTCUT("script_text_editor/goto_previous_breakpoint", TTR("Go to Previous Breakpoint"), KEY_MASK_CMD | KEY_COMMA); ED_SHORTCUT("script_text_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F); #ifdef OSX_ENABLED @@ -1581,14 +1581,14 @@ void ScriptTextEditor::register_editor() { ED_SHORTCUT("script_text_editor/replace", TTR("Replace..."), KEY_MASK_CMD | KEY_R); #endif - ED_SHORTCUT("script_text_editor/find_in_files", TTR("Find in files..."), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F); + ED_SHORTCUT("script_text_editor/find_in_files", TTR("Find in Files..."), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F); #ifdef OSX_ENABLED - ED_SHORTCUT("script_text_editor/goto_function", TTR("Goto Function..."), KEY_MASK_CTRL | KEY_MASK_CMD | KEY_J); + ED_SHORTCUT("script_text_editor/goto_function", TTR("Go to Function..."), KEY_MASK_CTRL | KEY_MASK_CMD | KEY_J); #else - ED_SHORTCUT("script_text_editor/goto_function", TTR("Goto Function..."), KEY_MASK_ALT | KEY_MASK_CMD | KEY_F); + ED_SHORTCUT("script_text_editor/goto_function", TTR("Go to Function..."), KEY_MASK_ALT | KEY_MASK_CMD | KEY_F); #endif - ED_SHORTCUT("script_text_editor/goto_line", TTR("Goto Line..."), KEY_MASK_CMD | KEY_L); + ED_SHORTCUT("script_text_editor/goto_line", TTR("Go to Line..."), KEY_MASK_CMD | KEY_L); #ifdef OSX_ENABLED ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KEY_MASK_ALT | KEY_MASK_SHIFT | KEY_SPACE); 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..271f753003 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -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; @@ -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..532b3915a7 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); @@ -975,24 +981,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; } } diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index cc477314e9..f18c43b0d0 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); 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_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/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..d815898ffd 100644 --- a/modules/bullet/space_bullet.h +++ b/modules/bullet/space_bullet.h @@ -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/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp index 0a1061f92e..7b5fd854ff 100644 --- a/modules/enet/networked_multiplayer_enet.cpp +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -207,13 +207,13 @@ void NetworkedMultiplayerENet::poll() { _pop_current_packet(); ENetEvent event; - /* Wait up to 1000 milliseconds for an event. */ + /* Keep servicing until there are no available events left in queue. */ while (true) { if (!host || !active) // Might have been disconnected while emitting a notification return; - int ret = enet_host_service(host, &event, 1); + int ret = enet_host_service(host, &event, 0); if (ret < 0) { // Error, do something? @@ -293,7 +293,7 @@ void NetworkedMultiplayerENet::poll() { encode_uint32(*id, &packet->data[4]); enet_peer_send(E->get(), SYSCH_CONFIG, packet); } - } else if (!server) { + } else { emit_signal("server_disconnected"); close_connection(); return; 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..d2f0ad042a 100644 --- a/modules/freetype/SCsub +++ b/modules/freetype/SCsub @@ -73,6 +73,8 @@ if env['builtin_freetype']: if env['builtin_libpng']: env.Append(CPPPATH=["#thirdparty/libpng"]) + # FIXME: Find a way to build this in a separate env nevertheless + # so that we can disable warnings on thirdparty code 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 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..5facfe7869 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)); diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index cd68072499..c7813a2144 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; } 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/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index c013c232d4..91fd482235 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -1174,7 +1174,7 @@ bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) { GDMonoProperty *property = script->script_class->get_property(p_name); if (property) { - property->set_value(mono_object, GDMonoMarshal::variant_to_mono_object(p_value)); + property->set_value(mono_object, GDMonoMarshal::variant_to_mono_object(p_value, property->get_type())); return true; } diff --git a/modules/mono/glue/Managed/Files/Array.cs b/modules/mono/glue/Managed/Files/Array.cs index c80cb7cc83..d5a35d7ae0 100644 --- a/modules/mono/glue/Managed/Files/Array.cs +++ b/modules/mono/glue/Managed/Files/Array.cs @@ -128,7 +128,7 @@ namespace Godot.Collections for (int i = 0; i < count; i++) { - yield return godot_icall_Array_At(GetPtr(), i); + yield return this[i]; } } @@ -167,6 +167,9 @@ namespace Godot.Collections internal extern static object godot_icall_Array_At(IntPtr ptr, int index); [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static object godot_icall_Array_At_Generic(IntPtr ptr, int index, int elemTypeEncoding, IntPtr elemTypeClass); + + [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_Array_SetAt(IntPtr ptr, int index, object value); [MethodImpl(MethodImplOptions.InternalCall)] @@ -195,12 +198,23 @@ namespace Godot.Collections [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_Array_RemoveAt(IntPtr ptr, int index); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_Generic_GetElementTypeInfo(Type elemType, out int elemTypeEncoding, out IntPtr elemTypeClass); } public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T> { Array objectArray; + internal static int elemTypeEncoding; + internal static IntPtr elemTypeClass; + + static Array() + { + Array.godot_icall_Array_Generic_GetElementTypeInfo(typeof(T), out elemTypeEncoding, out elemTypeClass); + } + public Array() { objectArray = new Array(); @@ -230,7 +244,7 @@ namespace Godot.Collections { get { - return (T)objectArray[index]; + return (T)Array.godot_icall_Array_At_Generic(GetPtr(), index, elemTypeEncoding, elemTypeClass); } set { @@ -287,7 +301,7 @@ namespace Godot.Collections for (int i = 0; i < count; i++) { - array[arrayIndex] = (T)objectArray[i]; + array[arrayIndex] = (T)this[i]; arrayIndex++; } } @@ -298,7 +312,7 @@ namespace Godot.Collections for (int i = 0; i < count; i++) { - yield return (T)objectArray[i]; + yield return (T)this[i]; } } diff --git a/modules/mono/glue/Managed/Files/Dictionary.cs b/modules/mono/glue/Managed/Files/Dictionary.cs index 523e48c31a..7695f03cd6 100644 --- a/modules/mono/glue/Managed/Files/Dictionary.cs +++ b/modules/mono/glue/Managed/Files/Dictionary.cs @@ -204,6 +204,9 @@ namespace Godot.Collections internal extern static object godot_icall_Dictionary_GetValue(IntPtr ptr, object key); [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static object godot_icall_Dictionary_GetValue_Generic(IntPtr ptr, object key, int valTypeEncoding, IntPtr valTypeClass); + + [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_Dictionary_SetValue(IntPtr ptr, object key, object value); [MethodImpl(MethodImplOptions.InternalCall)] @@ -235,6 +238,12 @@ namespace Godot.Collections [MethodImpl(MethodImplOptions.InternalCall)] internal extern static bool godot_icall_Dictionary_TryGetValue(IntPtr ptr, object key, out object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_TryGetValue_Generic(IntPtr ptr, object key, out object value, int valTypeEncoding, IntPtr valTypeClass); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_Generic_GetValueTypeInfo(Type valueType, out int valTypeEncoding, out IntPtr valTypeClass); } public class Dictionary<TKey, TValue> : @@ -244,6 +253,14 @@ namespace Godot.Collections { Dictionary objectDict; + internal static int valTypeEncoding; + internal static IntPtr valTypeClass; + + static Dictionary() + { + Dictionary.godot_icall_Dictionary_Generic_GetValueTypeInfo(typeof(TValue), out valTypeEncoding, out valTypeClass); + } + public Dictionary() { objectDict = new Dictionary(); @@ -273,7 +290,7 @@ namespace Godot.Collections { get { - return (TValue)objectDict[key]; + return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(objectDict.GetPtr(), key, valTypeEncoding, valTypeClass); } set { @@ -382,7 +399,7 @@ namespace Godot.Collections public bool TryGetValue(TKey key, out TValue value) { object retValue; - bool found = objectDict.TryGetValue(key, out retValue); + bool found = Dictionary.godot_icall_Dictionary_TryGetValue_Generic(GetPtr(), key, out retValue, valTypeEncoding, valTypeClass); value = found ? (TValue)retValue : default(TValue); return found; } diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp index d9dba1c60d..059e2ff6de 100644 --- a/modules/mono/glue/collections_glue.cpp +++ b/modules/mono/glue/collections_glue.cpp @@ -53,6 +53,14 @@ MonoObject *godot_icall_Array_At(Array *ptr, int index) { return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index)); } +MonoObject *godot_icall_Array_At_Generic(Array *ptr, int index, uint32_t type_encoding, GDMonoClass *type_class) { + if (index < 0 || index > ptr->size()) { + GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); + return NULL; + } + return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index), ManagedType(type_encoding, type_class)); +} + void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value) { if (index < 0 || index > ptr->size()) { GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); @@ -122,6 +130,14 @@ void godot_icall_Array_RemoveAt(Array *ptr, int index) { ptr->remove(index); } +void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class) { + MonoType *elem_type = mono_reflection_type_get_type(refltype); + + *type_encoding = mono_type_get_type(elem_type); + MonoClass *type_class_raw = mono_class_from_mono_type(elem_type); + *type_class = GDMono::get_singleton()->get_class(type_class_raw); +} + Dictionary *godot_icall_Dictionary_Ctor() { return memnew(Dictionary); } @@ -144,6 +160,20 @@ MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key) { return GDMonoMarshal::variant_to_mono_object(ret); } +MonoObject *godot_icall_Dictionary_GetValue_Generic(Dictionary *ptr, MonoObject *key, uint32_t type_encoding, GDMonoClass *type_class) { + Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); + if (ret == NULL) { + MonoObject *exc = mono_object_new(mono_domain_get(), CACHED_CLASS(KeyNotFoundException)->get_mono_ptr()); +#ifdef DEBUG_ENABLED + CRASH_COND(!exc); +#endif + GDMonoUtils::runtime_object_init(exc); + GDMonoUtils::set_pending_exception((MonoException *)exc); + return NULL; + } + return GDMonoMarshal::variant_to_mono_object(ret, ManagedType(type_encoding, type_class)); +} + void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value) { ptr->operator[](GDMonoMarshal::mono_object_to_variant(key)) = GDMonoMarshal::mono_object_to_variant(value); } @@ -211,10 +241,29 @@ bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoOb return true; } +bool godot_icall_Dictionary_TryGetValue_Generic(Dictionary *ptr, MonoObject *key, MonoObject **value, uint32_t type_encoding, GDMonoClass *type_class) { + Variant *ret = ptr->getptr(GDMonoMarshal::mono_object_to_variant(key)); + if (ret == NULL) { + *value = NULL; + return false; + } + *value = GDMonoMarshal::variant_to_mono_object(ret, ManagedType(type_encoding, type_class)); + return true; +} + +void godot_icall_Dictionary_Generic_GetValueTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class) { + MonoType *value_type = mono_reflection_type_get_type(refltype); + + *type_encoding = mono_type_get_type(value_type); + MonoClass *type_class_raw = mono_class_from_mono_type(value_type); + *type_class = GDMono::get_singleton()->get_class(type_class_raw); +} + void godot_register_collections_icalls() { mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", (void *)godot_icall_Array_Ctor); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Dtor", (void *)godot_icall_Array_Dtor); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_At", (void *)godot_icall_Array_At); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_At_Generic", (void *)godot_icall_Array_At_Generic); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_SetAt", (void *)godot_icall_Array_SetAt); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Count", (void *)godot_icall_Array_Count); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Add", (void *)godot_icall_Array_Add); @@ -225,10 +274,12 @@ void godot_register_collections_icalls() { mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Insert", (void *)godot_icall_Array_Insert); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Remove", (void *)godot_icall_Array_Remove); mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_RemoveAt", (void *)godot_icall_Array_RemoveAt); + mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Generic_GetElementTypeInfo", (void *)godot_icall_Array_Generic_GetElementTypeInfo); mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Ctor", (void *)godot_icall_Dictionary_Ctor); mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Dtor", (void *)godot_icall_Dictionary_Dtor); mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue", (void *)godot_icall_Dictionary_GetValue); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue_Generic", (void *)godot_icall_Dictionary_GetValue_Generic); mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_SetValue", (void *)godot_icall_Dictionary_SetValue); mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Keys", (void *)godot_icall_Dictionary_Keys); mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Values", (void *)godot_icall_Dictionary_Values); @@ -240,6 +291,8 @@ void godot_register_collections_icalls() { mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_RemoveKey", (void *)godot_icall_Dictionary_RemoveKey); mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Remove", (void *)godot_icall_Dictionary_Remove); mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue", (void *)godot_icall_Dictionary_TryGetValue); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue_Generic", (void *)godot_icall_Dictionary_TryGetValue_Generic); + mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Generic_GetValueTypeInfo", (void *)godot_icall_Dictionary_Generic_GetValueTypeInfo); } #endif // MONO_GLUE_ENABLED diff --git a/modules/mono/glue/collections_glue.h b/modules/mono/glue/collections_glue.h index fa8e4c28aa..b9b1338510 100644 --- a/modules/mono/glue/collections_glue.h +++ b/modules/mono/glue/collections_glue.h @@ -45,6 +45,8 @@ void godot_icall_Array_Dtor(Array *ptr); MonoObject *godot_icall_Array_At(Array *ptr, int index); +MonoObject *godot_icall_Array_At_Generic(Array *ptr, int index, uint32_t type_encoding, GDMonoClass *type_class); + void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value); int godot_icall_Array_Count(Array *ptr); @@ -65,6 +67,8 @@ bool godot_icall_Array_Remove(Array *ptr, MonoObject *item); void godot_icall_Array_RemoveAt(Array *ptr, int index); +void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class); + // Dictionary Dictionary *godot_icall_Dictionary_Ctor(); @@ -73,6 +77,8 @@ void godot_icall_Dictionary_Dtor(Dictionary *ptr); MonoObject *godot_icall_Dictionary_GetValue(Dictionary *ptr, MonoObject *key); +MonoObject *godot_icall_Dictionary_GetValue_Generic(Dictionary *ptr, MonoObject *key, uint32_t type_encoding, GDMonoClass *type_class); + void godot_icall_Dictionary_SetValue(Dictionary *ptr, MonoObject *key, MonoObject *value); Array *godot_icall_Dictionary_Keys(Dictionary *ptr); @@ -95,6 +101,10 @@ bool godot_icall_Dictionary_Remove(Dictionary *ptr, MonoObject *key, MonoObject bool godot_icall_Dictionary_TryGetValue(Dictionary *ptr, MonoObject *key, MonoObject **value); +bool godot_icall_Dictionary_TryGetValue_Generic(Dictionary *ptr, MonoObject *key, MonoObject **value, uint32_t type_encoding, GDMonoClass *type_class); + +void godot_icall_Dictionary_Generic_GetValueTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class); + // Register internal calls void godot_register_collections_icalls(); diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 2fed6064b7..9311aa3930 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -259,14 +259,15 @@ void GDMono::initialize() { // The following assemblies are not required at initialization #ifdef MONO_GLUE_ENABLED if (_load_api_assemblies()) { - if (!core_api_assembly_out_of_sync && !editor_api_assembly_out_of_sync && GDMonoUtils::mono_cache.godot_api_cache_updated) { - // Everything is fine with the api assemblies, load the project assembly - _load_project_assembly(); - } else { + // Everything is fine with the api assemblies, load the project assembly + _load_project_assembly(); + } else { + if ((core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated)) || + (editor_api_assembly && editor_api_assembly_out_of_sync)) { #ifdef TOOLS_ENABLED // The assembly was successfully loaded, but the full api could not be cached. - // This is most likely an outdated assembly loaded because of an invalid version in the metadata, - // so we invalidate the version in the metadata and unload the script domain. + // This is most likely an outdated assembly loaded because of an invalid version in the + // metadata, so we invalidate the version in the metadata and unload the script domain. if (core_api_assembly_out_of_sync) { ERR_PRINT("The loaded Core API assembly is out of sync"); @@ -290,12 +291,12 @@ void GDMono::initialize() { #else ERR_PRINT("The loaded API assembly is invalid"); CRASH_NOW(); -#endif +#endif // TOOLS_ENABLED } } #else print_verbose("Mono: Glue disabled, ignoring script assemblies."); -#endif +#endif // MONO_GLUE_ENABLED print_verbose("Mono: INITIALIZED"); } @@ -448,8 +449,10 @@ bool GDMono::_load_core_api_assembly() { return true; #ifdef TOOLS_ENABLED - if (metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) + if (metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) { + print_verbose("Mono: Skipping loading of Core API assembly because it was invalidated"); return false; + } #endif bool success = load_assembly(API_ASSEMBLY_NAME, &core_api_assembly); @@ -460,8 +463,12 @@ bool GDMono::_load_core_api_assembly() { core_api_assembly_out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash || GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version || CS_GLUE_VERSION != api_assembly_ver.cs_glue_version; -#endif + if (!core_api_assembly_out_of_sync) { + GDMonoUtils::update_godot_api_cache(); + } +#else GDMonoUtils::update_godot_api_cache(); +#endif } return success; @@ -474,8 +481,10 @@ bool GDMono::_load_editor_api_assembly() { return true; #ifdef TOOLS_ENABLED - if (metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) + if (metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) { + print_verbose("Mono: Skipping loading of Editor API assembly because it was invalidated"); return false; + } #endif bool success = load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly); @@ -533,16 +542,22 @@ bool GDMono::_load_api_assemblies() { if (OS::get_singleton()->is_stdout_verbose()) print_error("Mono: Failed to load Core API assembly"); return false; - } else { + } + + if (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated) + return false; + #ifdef TOOLS_ENABLED - if (!_load_editor_api_assembly()) { - if (OS::get_singleton()->is_stdout_verbose()) - print_error("Mono: Failed to load Editor API assembly"); - return false; - } -#endif + if (!_load_editor_api_assembly()) { + if (OS::get_singleton()->is_stdout_verbose()) + print_error("Mono: Failed to load Editor API assembly"); + return false; } + if (editor_api_assembly_out_of_sync) + return false; +#endif + return true; } @@ -708,43 +723,42 @@ Error GDMono::reload_scripts_domain() { #ifdef MONO_GLUE_ENABLED if (!_load_api_assemblies()) { - return ERR_CANT_OPEN; - } + if ((core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated)) || + (editor_api_assembly && editor_api_assembly_out_of_sync)) { + // The assembly was successfully loaded, but the full api could not be cached. + // This is most likely an outdated assembly loaded because of an invalid version in the + // metadata, so we invalidate the version in the metadata and unload the script domain. - if (!core_api_assembly_out_of_sync && !editor_api_assembly_out_of_sync && GDMonoUtils::mono_cache.godot_api_cache_updated) { - // Everything is fine with the api assemblies, load the project assembly - _load_project_assembly(); - } else { - // The assembly was successfully loaded, but the full api could not be cached. - // This is most likely an outdated assembly loaded because of an invalid version in the metadata, - // so we invalidate the version in the metadata and unload the script domain. - - if (core_api_assembly_out_of_sync) { - ERR_PRINT("The loaded Core API assembly is out of sync"); - metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); - } else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) { - ERR_PRINT("The loaded Core API assembly is in sync, but the cache update failed"); - metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); - } + if (core_api_assembly_out_of_sync) { + ERR_PRINT("The loaded Core API assembly is out of sync"); + metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); + } else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) { + ERR_PRINT("The loaded Core API assembly is in sync, but the cache update failed"); + metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true); + } - if (editor_api_assembly_out_of_sync) { - ERR_PRINT("The loaded Editor API assembly is out of sync"); - metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true); - } + if (editor_api_assembly_out_of_sync) { + ERR_PRINT("The loaded Editor API assembly is out of sync"); + metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true); + } - Error err = _unload_scripts_domain(); - if (err != OK) { - WARN_PRINT("Mono: Failed to unload scripts domain"); - } + Error err = _unload_scripts_domain(); + if (err != OK) { + WARN_PRINT("Mono: Failed to unload scripts domain"); + } - return ERR_CANT_RESOLVE; + return ERR_CANT_RESOLVE; + } else { + return ERR_CANT_OPEN; + } } - if (!_load_project_assembly()) + if (!_load_project_assembly()) { return ERR_CANT_OPEN; + } #else print_verbose("Mono: Glue disabled, ignoring script assemblies."); -#endif +#endif // MONO_GLUE_ENABLED return OK; } diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h index 2fe05006f1..4f2efc7b92 100644 --- a/modules/mono/mono_gd/gd_mono_header.h +++ b/modules/mono/mono_gd/gd_mono_header.h @@ -44,9 +44,14 @@ struct ManagedType { int type_encoding; GDMonoClass *type_class; - ManagedType() { - type_encoding = 0; - type_class = NULL; + ManagedType() : + type_encoding(0), + type_class(NULL) { + } + + ManagedType(int p_type_encoding, GDMonoClass *p_type_class) : + type_encoding(p_type_encoding), + type_class(p_type_class) { } }; diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 1ad0a4a6ea..cc0ab5fa05 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -97,10 +97,14 @@ _FORCE_INLINE_ MonoString *mono_string_from_godot(const String &p_string) { MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_type); MonoObject *variant_to_mono_object(const Variant *p_var); -_FORCE_INLINE_ MonoObject *variant_to_mono_object(Variant p_var) { +_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant &p_var) { return variant_to_mono_object(&p_var); } +_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_type) { + return variant_to_mono_object(&p_var, p_type); +} + Variant mono_object_to_variant(MonoObject *p_obj); // Array 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..d14191056f 100644 --- a/modules/svg/SCsub +++ b/modules/svg/SCsub @@ -9,11 +9,12 @@ thirdparty_sources = [ ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env.add_source_files(env.modules_sources, thirdparty_sources) env.Append(CPPPATH=[thirdparty_dir]) env.Append(CCFLAGS=["-DSVG_ENABLED"]) +env_thirdparty = env.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources) + # Godot's own source files env.add_source_files(env.modules_sources, "*.cpp") - -Export('env') 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/thekla_unwrap/config.py b/modules/thekla_unwrap/config.py index bd092bdc16..fad6095064 100644 --- a/modules/thekla_unwrap/config.py +++ b/modules/thekla_unwrap/config.py @@ -1,5 +1,6 @@ def can_build(env, platform): - return (env['tools'] and platform not in ["android", "ios"]) + #return (env['tools'] and platform not in ["android", "ios"]) + return False def configure(env): pass 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..e2e5cc77f5 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; } @@ -721,6 +718,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 2daf8c282f..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' @@ -349,7 +350,7 @@ if webm_multithread: env_libvpx.add_source_files(env.modules_sources, libvpx_sources_mt) if webm_cpu_x86: - is_clang_or_gcc = ('gcc' in env["CC"]) or ('clang' in env["CC"]) or ("OSXCROSS_ROOT" in os.environ) + is_clang_or_gcc = ('gcc' in os.path.basename(env["CC"])) or ('clang' in os.path.basename(env["CC"])) or ("OSXCROSS_ROOT" in os.environ) env_libvpx_mmx = env_libvpx.Clone() if cpu_bits == '32' and is_clang_or_gcc: 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..bd092bdc16 --- /dev/null +++ b/modules/xatlas_unwrap/config.py @@ -0,0 +1,5 @@ +def can_build(env, platform): + 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..683dbfdc5a 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()); @@ -1082,7 +1081,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 +1485,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 +1619,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/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/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/crash_handler_osx.mm b/platform/osx/crash_handler_osx.mm index 9ad3437f0f..490155bb24 100644 --- a/platform/osx/crash_handler_osx.mm +++ b/platform/osx/crash_handler_osx.mm @@ -68,8 +68,9 @@ static uint64_t load_address() { } static void handle_crash(int sig) { - if (OS::get_singleton() == NULL) - return; + if (OS::get_singleton() == NULL) { + abort(); + } void *bt_buffer[256]; size_t size = backtrace(bt_buffer, 256); @@ -151,6 +152,7 @@ CrashHandler::CrashHandler() { } CrashHandler::~CrashHandler() { + disable(); } void CrashHandler::disable() { 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/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 266b0c5cc9..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): @@ -56,7 +54,7 @@ def configure(env): ## Compiler configuration if env['use_llvm']: - if ('clang++' not in env['CXX']): + if ('clang++' not in os.path.basename(env['CXX'])): env["CC"] = "clang" env["CXX"] = "clang++" env["LINK"] = "clang++" 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/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/crash_handler_x11.cpp b/platform/x11/crash_handler_x11.cpp index ab9275e49f..79c3d9aece 100644 --- a/platform/x11/crash_handler_x11.cpp +++ b/platform/x11/crash_handler_x11.cpp @@ -45,8 +45,9 @@ #include <stdlib.h> static void handle_crash(int sig) { - if (OS::get_singleton() == NULL) - return; + if (OS::get_singleton() == NULL) { + abort(); + } void *bt_buffer[256]; size_t size = backtrace(bt_buffer, 256); @@ -119,6 +120,7 @@ CrashHandler::CrashHandler() { } CrashHandler::~CrashHandler() { + disable(); } void CrashHandler::disable() { diff --git a/platform/x11/detect.py b/platform/x11/detect.py index 6a7a426804..905546e724 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -86,7 +86,7 @@ def configure(env): env.Prepend(CCFLAGS=['-O3', '-ffast-math']) else: #optimize for size env.Prepend(CCFLAGS=['-Os']) - + if (env["debug_symbols"] == "yes"): env.Prepend(CCFLAGS=['-g1']) if (env["debug_symbols"] == "full"): @@ -115,12 +115,12 @@ def configure(env): ## Compiler configuration - if 'CXX' in env and 'clang' in env['CXX']: + if 'CXX' in env and 'clang' in os.path.basename(env['CXX']): # Convenience check to enforce the use_llvm overrides when CXX is clang(++) env['use_llvm'] = True if env['use_llvm']: - if ('clang++' not in env['CXX']): + if ('clang++' not in os.path.basename(env['CXX'])): env["CC"] = "clang" env["CXX"] = "clang++" env["LINK"] = "clang++" 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..895e76d516 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -360,7 +360,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 +538,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/control.cpp b/scene/gui/control.cpp index 12349e0983..dc18895298 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -1993,7 +1993,7 @@ Control *Control::find_prev_valid_focus() const { if (!from) { - ERR_EXPLAIN("Prev focus node is not a control: " + n->get_name()); + ERR_EXPLAIN("Previous focus node is not a control: " + n->get_name()); ERR_FAIL_V(NULL); } } else { 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 c61bb25fb9..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; } @@ -5156,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; @@ -5168,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)) { @@ -5870,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..fa87623e38 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -546,19 +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, 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/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_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_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..ef2b767a8a 100644 --- a/servers/visual/visual_server_raster.h +++ b/servers/visual/visual_server_raster.h @@ -337,6 +337,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 +690,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") |