diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index ec159da00f03..960cdbac2025 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -98,6 +98,13 @@ void _ResourceSaver::_bind_methods() { ObjectTypeDB::bind_method(_MD("save","path","resource:Resource"),&_ResourceSaver::save, DEFVAL(0)); ObjectTypeDB::bind_method(_MD("get_recognized_extensions","type"),&_ResourceSaver::get_recognized_extensions); + + BIND_CONSTANT(FLAG_RELATIVE_PATHS); + BIND_CONSTANT(FLAG_BUNDLE_RESOURCES); + BIND_CONSTANT(FLAG_CHANGE_PATH); + BIND_CONSTANT(FLAG_OMIT_EDITOR_PROPERTIES); + BIND_CONSTANT(FLAG_SAVE_BIG_ENDIAN); + BIND_CONSTANT(FLAG_COMPRESS); } _ResourceSaver::_ResourceSaver() { diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 18eb59476072..bb68bbaad8aa 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -39,6 +39,16 @@ protected: static _ResourceSaver *singleton; public: + enum SaverFlags { + + FLAG_RELATIVE_PATHS=1, + FLAG_BUNDLE_RESOURCES=2, + FLAG_CHANGE_PATH=4, + FLAG_OMIT_EDITOR_PROPERTIES=8, + FLAG_SAVE_BIG_ENDIAN=16, + FLAG_COMPRESS=32, + }; + static _ResourceSaver *get_singleton() { return singleton; } Error save(const String &p_path,const RES& p_resource, uint32_t p_flags); diff --git a/core/image.cpp b/core/image.cpp index ccabd04d6f15..db20862af5c8 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -1660,6 +1660,31 @@ void Image::set_compress_bc_func(void (*p_compress_func)(Image *)) { } + +void Image::premultiply_alpha() { + + if (data.size()==0) + return; + + if (format!=FORMAT_RGBA) + return; //not needed + + DVector::Write wp = data.write(); + unsigned char *data_ptr=wp.ptr(); + + + for(int i=0;i>8; + bc.g=(int(bc.g)*int(bc.a))>>8; + bc.b=(int(bc.b)*int(bc.a))>>8; + _put_pixel(j,i,bc,data_ptr); + } + } +} + void Image::fix_alpha_edges() { if (data.size()==0) diff --git a/core/image.h b/core/image.h index 186aceb1bfbb..99300fc3af40 100644 --- a/core/image.h +++ b/core/image.h @@ -320,6 +320,7 @@ public: void decompress(); void fix_alpha_edges(); + void premultiply_alpha(); void blit_rect(const Image& p_src, const Rect2& p_src_rect,const Point2& p_dest); void brush_transfer(const Image& p_src, const Image& p_brush, const Point2& p_dest); diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index bcd4197e1155..29f27dcbdab0 100644 --- a/core/io/file_access_encrypted.cpp +++ b/core/io/file_access_encrypted.cpp @@ -25,6 +25,7 @@ Error FileAccessEncrypted::open_and_parse(FileAccess *p_base,const Vectorget_32(); print_line("MAGIC: "+itos(magic)); @@ -278,6 +279,10 @@ uint64_t FileAccessEncrypted::_get_modified_time(const String& p_file){ FileAccessEncrypted::FileAccessEncrypted() { file=NULL; + pos=0; + eofed=false; + mode=MODE_MAX; + writing=false; } diff --git a/core/io/ip.cpp b/core/io/ip.cpp index 503a009444cb..d2a685f6b085 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -188,6 +188,18 @@ void IP::erase_resolve_item(ResolverID p_id) { } +Array IP::_get_local_addresses() const { + + Array addresses; + List ip_addresses; + get_local_addresses(&ip_addresses); + for(List::Element *E=ip_addresses.front();E;E=E->next()) { + addresses.push_back(E->get()); + } + + return addresses; +} + void IP::_bind_methods() { ObjectTypeDB::bind_method(_MD("resolve_hostname","host"),&IP::resolve_hostname); @@ -195,6 +207,7 @@ void IP::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_resolve_item_status","id"),&IP::get_resolve_item_status); ObjectTypeDB::bind_method(_MD("get_resolve_item_address","id"),&IP::get_resolve_item_address); ObjectTypeDB::bind_method(_MD("erase_resolve_item","id"),&IP::erase_resolve_item); + ObjectTypeDB::bind_method(_MD("get_local_addresses"),&IP::_get_local_addresses); BIND_CONSTANT( RESOLVER_STATUS_NONE ); BIND_CONSTANT( RESOLVER_STATUS_WAITING ); diff --git a/core/io/ip.h b/core/io/ip.h index f1ef5fe794cc..0181dc7d12fd 100644 --- a/core/io/ip.h +++ b/core/io/ip.h @@ -66,16 +66,19 @@ protected: static void _bind_methods(); virtual IP_Address _resolve_hostname(const String& p_hostname)=0; + Array _get_local_addresses() const; static IP* (*_create)(); public: + IP_Address resolve_hostname(const String& p_hostname); // async resolver hostname ResolverID resolve_hostname_queue_item(const String& p_hostname); ResolverStatus get_resolve_item_status(ResolverID p_id) const; IP_Address get_resolve_item_address(ResolverID p_id) const; + virtual void get_local_addresses(List *r_addresses) const=0; void erase_resolve_item(ResolverID p_id); static IP* get_singleton(); diff --git a/core/os/file_access.cpp b/core/os/file_access.cpp index 31e7d19bae69..ffa0cad8e440 100644 --- a/core/os/file_access.cpp +++ b/core/os/file_access.cpp @@ -100,7 +100,7 @@ FileAccess *FileAccess::open(const String& p_path, int p_mode_flags, Error *r_er FileAccess *ret=NULL; if (!(p_mode_flags&WRITE) && PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled()) { ret = PackedData::get_singleton()->try_open_path(p_path); - if (ret) { + if (ret) { if (r_error) *r_error=OK; return ret; diff --git a/core/os/input.cpp b/core/os/input.cpp index d7c0d86d6458..70733aadeca2 100644 --- a/core/os/input.cpp +++ b/core/os/input.cpp @@ -211,6 +211,8 @@ void InputDefault::parse_input_event(const InputEvent& p_event) { if (p_event.key.scancode==0) break; + // print_line(p_event); + if (p_event.key.pressed) keys_pressed.insert(p_event.key.scancode); else diff --git a/core/os/main_loop.cpp b/core/os/main_loop.cpp index a8e02526b965..d01331a2561e 100644 --- a/core/os/main_loop.cpp +++ b/core/os/main_loop.cpp @@ -37,7 +37,7 @@ void MainLoop::_bind_methods() { BIND_CONSTANT(NOTIFICATION_WM_FOCUS_OUT); BIND_CONSTANT(NOTIFICATION_WM_QUIT_REQUEST); BIND_CONSTANT(NOTIFICATION_WM_UNFOCUS_REQUEST); - + BIND_CONSTANT(NOTIFICATION_OS_MEMORY_WARNING); }; diff --git a/core/script_language.h b/core/script_language.h index 973127361005..560de520ca2b 100644 --- a/core/script_language.h +++ b/core/script_language.h @@ -141,6 +141,7 @@ public: virtual int find_function(const String& p_function,const String& p_code) const=0; virtual String make_function(const String& p_class,const String& p_name,const StringArray& p_args) const=0; virtual Error complete_keyword(const String& p_code, int p_line, const String& p_base_path, const String& p_keyword, List* r_options) { return ERR_UNAVAILABLE; } + virtual void auto_indent_code(String& p_code,int p_from_line,int p_to_line) const=0; /* DEBUGGER FUNCTIONS */ diff --git a/demos/2d/platformer/stage.xml b/demos/2d/platformer/stage.xml index 952c21e936eb..6a112e02aa5a 100644 --- a/demos/2d/platformer/stage.xml +++ b/demos/2d/platformer/stage.xml @@ -3,10 +3,10 @@ + + - - @@ -160,9 +160,13 @@ "pixel_snap" False "zoom" - 0.735092 + 0.54036 + "use_snap" + False "ofs" - 55.9232, 767.661 + 418.81, 615.088 + "snap" + 10 "3D" diff --git a/doc/make_doc.sh b/doc/make_doc.sh old mode 100755 new mode 100644 diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp index f43b43bd77ef..472e92f23a0c 100644 --- a/drivers/gles2/rasterizer_gles2.cpp +++ b/drivers/gles2/rasterizer_gles2.cpp @@ -6436,7 +6436,10 @@ void RasterizerGLES2::canvas_set_blend_mode(VS::MaterialBlendMode p_mode) { case VS::MATERIAL_BLEND_MODE_MUL: { glBlendEquation(GL_FUNC_ADD); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); - + } break; + case VS::MATERIAL_BLEND_MODE_PREMULT_ALPHA: { + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA); } break; } diff --git a/drivers/gles2/shaders/canvas.glsl b/drivers/gles2/shaders/canvas.glsl index 66bdf15db453..f36741d58620 100644 --- a/drivers/gles2/shaders/canvas.glsl +++ b/drivers/gles2/shaders/canvas.glsl @@ -64,6 +64,9 @@ void main() { highp float enc32 = dot( color,highp vec4(1.0 / (256.0 * 256.0 * 256.0),1.0 / (256.0 * 256.0),1.0 / 256.0,1) ); color = vec4(vec3(enc32),1.0); #endif + +// color.rgb*=color.a; gl_FragColor = color; + } diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp index c11426d3563a..18b19ca0950b 100644 --- a/drivers/unix/ip_unix.cpp +++ b/drivers/unix/ip_unix.cpp @@ -30,12 +30,24 @@ #if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED) + #ifdef WINDOWS_ENABLED +#define WINVER 0x0600 #include #include #include +#include +#include #else #include +#ifdef ANDROID_ENABLED +#include "platform/android/ifaddrs_android.h" +#else +#include +#endif +#include +#include + #endif IP_Address IP_Unix::_resolve_hostname(const String& p_hostname) { @@ -52,6 +64,93 @@ IP_Address IP_Unix::_resolve_hostname(const String& p_hostname) { } +#if defined(WINDOWS_ENABLED) + +void IP_Unix::get_local_addresses(List *r_addresses) const { + + ULONG buf_size = 1024; + IP_ADAPTER_ADDRESSES* addrs; + + while (true) { + + addrs = (IP_ADAPTER_ADDRESSES*)memalloc(buf_size); + int err = GetAdaptersAddresses(AF_INET, GAA_FLAG_SKIP_ANYCAST | + GAA_FLAG_SKIP_MULTICAST | + GAA_FLAG_SKIP_DNS_SERVER | + GAA_FLAG_SKIP_FRIENDLY_NAME, + NULL, addrs, &buf_size); + if (err == NO_ERROR) { + break; + }; + memfree(addrs); + if (err == ERROR_BUFFER_OVERFLOW) { + continue; // will go back and alloc the right size + }; + + ERR_EXPLAIN("Call to GetAdaptersAddresses failed with error " + itos(err)); + ERR_FAIL(); + return; + }; + + + IP_ADAPTER_ADDRESSES* adapter = addrs; + + while (adapter != NULL) { + + IP_ADAPTER_UNICAST_ADDRESS* address = adapter->FirstUnicastAddress; + while (address != NULL) { + + char addr_chr[INET_ADDRSTRLEN]; + SOCKADDR_IN* ipv4 = reinterpret_cast(address->Address.lpSockaddr); + + IP_Address ip; + ip.host= *((unsigned long*)&ipv4->sin_addr); + + + //inet_ntop(AF_INET, &ipv4->sin_addr, addr_chr, INET_ADDRSTRLEN); + + r_addresses->push_back(ip); + + address = address->Next; + }; + adapter = adapter->Next; + }; + + memfree(addrs); +}; + + +#else + +void IP_Unix::get_local_addresses(List *r_addresses) const { + + struct ifaddrs * ifAddrStruct=NULL; + struct ifaddrs * ifa=NULL; + + getifaddrs(&ifAddrStruct); + + for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa ->ifa_addr->sa_family==AF_INET) { // check it is IP4 + // is a valid IP4 Address + + IP_Address ip; + ip.host= *((unsigned long*)&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr); + + r_addresses->push_back(ip); + }/* else if (ifa->ifa_addr->sa_family==AF_INET6) { // check it is IP6 + // is a valid IP6 Address + tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; + char addressBuffer[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN); + printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); + } */ + } + + if (ifAddrStruct!=NULL) freeifaddrs(ifAddrStruct); + +} +#endif + void IP_Unix::make_default() { _create=_create_unix; diff --git a/drivers/unix/ip_unix.h b/drivers/unix/ip_unix.h index ec2d42c83763..2fd5cf964bff 100644 --- a/drivers/unix/ip_unix.h +++ b/drivers/unix/ip_unix.h @@ -41,6 +41,8 @@ class IP_Unix : public IP { static IP* _create_unix(); public: + virtual void get_local_addresses(List *r_addresses) const; + static void make_default(); IP_Unix(); }; diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp index 48f009282721..548b10cf15ff 100644 --- a/modules/gdscript/gd_compiler.cpp +++ b/modules/gdscript/gd_compiler.cpp @@ -1263,6 +1263,16 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode * gdfunc->name=func_name; gdfunc->_script=p_script; gdfunc->source=source; + +#ifdef DEBUG_ENABLED + + { + gdfunc->func_cname=(String(source)+" - "+String(func_name)).utf8(); + gdfunc->_func_cname=gdfunc->func_cname.get_data(); + + } + +#endif if (p_func) { gdfunc->_initial_line=p_func->line; } else { diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp index f8717c292f99..5f5de8b5db29 100644 --- a/modules/gdscript/gd_editor.cpp +++ b/modules/gdscript/gd_editor.cpp @@ -787,3 +787,67 @@ Error GDScriptLanguage::complete_keyword(const String& p_code, int p_line, const return OK; } +void GDScriptLanguage::auto_indent_code(String& p_code,int p_from_line,int p_to_line) const { + + + Vector lines = p_code.split("\n"); + List indent_stack; + + for(int i=0;iget(); + } + + if (tc>ilevel) { + indent_stack.push_back(tc); + } else if (tcget()>tc) { + indent_stack.pop_back(); + } + + if (indent_stack.size() && indent_stack.back()->get()!=tc) + indent_stack.push_back(tc); //this is not right but gets the job done + } + + if (i>=p_from_line) { + + l=""; + for(int j=0;jp_to_line) { + break; + } + + //print_line(itos(indent_stack.size())+","+itos(tc)+": "+l); + lines[i]=l; + } + + p_code=""; + for(int i=0;i0) + p_code+="\n"; + p_code+=lines[i]; + } + +} diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp index f540660cd36f..2829132d99c6 100644 --- a/modules/gdscript/gd_parser.cpp +++ b/modules/gdscript/gd_parser.cpp @@ -1221,6 +1221,15 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { return; //go back a level } + if (pending_newline!=-1) { + + NewLineNode *nl = alloc_node(); + nl->line=pending_newline; + p_block->statements.push_back(nl); + pending_newline=-1; + + } + switch(token) { @@ -1234,16 +1243,19 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { } break; case GDTokenizer::TK_NEWLINE: { + if (!_parse_newline()) { + if (!error_set) { + p_block->end_line=tokenizer->get_token_line(); + pending_newline=p_block->end_line; + + } + return; + } + NewLineNode *nl = alloc_node(); nl->line=tokenizer->get_token_line(); p_block->statements.push_back(nl); - if (!_parse_newline()) { - if (!error_set) { - p_block->end_line=tokenizer->get_token_line(); - } - return; - } } break; case GDTokenizer::TK_CF_PASS: { if (tokenizer->get_token(1)!=GDTokenizer::TK_SEMICOLON && tokenizer->get_token(1)!=GDTokenizer::TK_NEWLINE ) { @@ -1782,6 +1794,7 @@ void GDParser::_parse_class(ClassNode *p_class) { case GDTokenizer::TK_PR_FUNCTION: { bool _static=false; + pending_newline=-1; if (tokenizer->get_token(-1)==GDTokenizer::TK_PR_STATIC) { @@ -2490,6 +2503,7 @@ void GDParser::clear() { tab_level.push_back(0); error_line=0; error_column=0; + pending_newline=-1; parenthesis=0; current_export.type=Variant::NIL; error=""; @@ -2501,6 +2515,7 @@ GDParser::GDParser() { head=NULL; list=NULL; tokenizer=NULL; + pending_newline=-1; clear(); } diff --git a/modules/gdscript/gd_parser.h b/modules/gdscript/gd_parser.h index 278e5f543d82..825bd954d13e 100644 --- a/modules/gdscript/gd_parser.h +++ b/modules/gdscript/gd_parser.h @@ -362,6 +362,8 @@ private: int error_line; int error_column; + int pending_newline; + List tab_level; String base_path; diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp index c6183aadc488..0d49f79f1c41 100644 --- a/modules/gdscript/gd_script.cpp +++ b/modules/gdscript/gd_script.cpp @@ -1159,6 +1159,9 @@ GDFunction::GDFunction() { _stack_size=0; _call_size=0; name=""; +#ifdef DEBUG_ENABLED + _func_cname=NULL; +#endif } diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h index 983899240ae9..56da0bb2e390 100644 --- a/modules/gdscript/gd_script.h +++ b/modules/gdscript/gd_script.h @@ -118,10 +118,13 @@ friend class GDCompiler; Vector constants; Vector global_names; Vector default_arguments; - Vector code; +#ifdef DEBUG_ENABLED + CharString func_cname; + const char*_func_cname; +#endif - List stack_debug; + List stack_debug; _FORCE_INLINE_ Variant *_get_variant(int p_address,GDInstance *p_instance,GDScript *p_script,Variant &self,Variant *p_stack,String& r_error) const; _FORCE_INLINE_ String _get_call_error(const Variant::CallError& p_err, const String& p_where,const Variant**argptrs) const; @@ -427,6 +430,7 @@ public: virtual int find_function(const String& p_function,const String& p_code) const; virtual String make_function(const String& p_class,const String& p_name,const StringArray& p_args) const; virtual Error complete_keyword(const String& p_code, int p_line, const String& p_base_path,const String& p_keyword, List* r_options); + virtual void auto_indent_code(String& p_code,int p_from_line,int p_to_line) const; /* DEBUGGER FUNCTIONS */ diff --git a/platform/android/SCsub b/platform/android/SCsub index 5464376c3191..8e61b7d8e0ee 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -13,6 +13,7 @@ android_files = [ 'dir_access_jandroid.cpp', 'thread_jandroid.cpp', 'audio_driver_jandroid.cpp', + 'ifaddrs_android.cpp', 'android_native_app_glue.c', 'java_glue.cpp' ] diff --git a/platform/android/ifaddrs_android.cpp b/platform/android/ifaddrs_android.cpp new file mode 100644 index 000000000000..c1e9eb3584af --- /dev/null +++ b/platform/android/ifaddrs_android.cpp @@ -0,0 +1,221 @@ +/* + * libjingle + * Copyright 2012, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ifaddrs_android.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +struct netlinkrequest { + nlmsghdr header; + ifaddrmsg msg; +}; +namespace { +const int kMaxReadSize = 4096; +}; +static int set_ifname(struct ifaddrs* ifaddr, int interface) { + char buf[IFNAMSIZ] = {0}; + char* name = if_indextoname(interface, buf); + if (name == NULL) { + return -1; + } + ifaddr->ifa_name = new char[strlen(name) + 1]; + strncpy(ifaddr->ifa_name, name, strlen(name) + 1); + return 0; +} +static int set_flags(struct ifaddrs* ifaddr) { + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) { + return -1; + } + ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifaddr->ifa_name, IFNAMSIZ - 1); + int rc = ioctl(fd, SIOCGIFFLAGS, &ifr); + close(fd); + if (rc == -1) { + return -1; + } + ifaddr->ifa_flags = ifr.ifr_flags; + return 0; +} +static int set_addresses(struct ifaddrs* ifaddr, ifaddrmsg* msg, void* data, + size_t len) { + if (msg->ifa_family == AF_INET) { + sockaddr_in* sa = new sockaddr_in; + sa->sin_family = AF_INET; + memcpy(&sa->sin_addr, data, len); + ifaddr->ifa_addr = reinterpret_cast(sa); + } else if (msg->ifa_family == AF_INET6) { + sockaddr_in6* sa = new sockaddr_in6; + sa->sin6_family = AF_INET6; + sa->sin6_scope_id = msg->ifa_index; + memcpy(&sa->sin6_addr, data, len); + ifaddr->ifa_addr = reinterpret_cast(sa); + } else { + return -1; + } + return 0; +} +static int make_prefixes(struct ifaddrs* ifaddr, int family, int prefixlen) { + char* prefix = NULL; + if (family == AF_INET) { + sockaddr_in* mask = new sockaddr_in; + mask->sin_family = AF_INET; + memset(&mask->sin_addr, 0, sizeof(in_addr)); + ifaddr->ifa_netmask = reinterpret_cast(mask); + if (prefixlen > 32) { + prefixlen = 32; + } + prefix = reinterpret_cast(&mask->sin_addr); + } else if (family == AF_INET6) { + sockaddr_in6* mask = new sockaddr_in6; + mask->sin6_family = AF_INET6; + memset(&mask->sin6_addr, 0, sizeof(in6_addr)); + ifaddr->ifa_netmask = reinterpret_cast(mask); + if (prefixlen > 128) { + prefixlen = 128; + } + prefix = reinterpret_cast(&mask->sin6_addr); + } else { + return -1; + } + for (int i = 0; i < (prefixlen / 8); i++) { + *prefix++ = 0xFF; + } + char remainder = 0xff; + remainder <<= (8 - prefixlen % 8); + *prefix = remainder; + return 0; +} +static int populate_ifaddrs(struct ifaddrs* ifaddr, ifaddrmsg* msg, void* bytes, + size_t len) { + if (set_ifname(ifaddr, msg->ifa_index) != 0) { + return -1; + } + if (set_flags(ifaddr) != 0) { + return -1; + } + if (set_addresses(ifaddr, msg, bytes, len) != 0) { + return -1; + } + if (make_prefixes(ifaddr, msg->ifa_family, msg->ifa_prefixlen) != 0) { + return -1; + } + return 0; +} +int getifaddrs(struct ifaddrs** result) { + int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (fd < 0) { + return -1; + } + netlinkrequest ifaddr_request; + memset(&ifaddr_request, 0, sizeof(ifaddr_request)); + ifaddr_request.header.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST; + ifaddr_request.header.nlmsg_type = RTM_GETADDR; + ifaddr_request.header.nlmsg_len = NLMSG_LENGTH(sizeof(ifaddrmsg)); + ssize_t count = send(fd, &ifaddr_request, ifaddr_request.header.nlmsg_len, 0); + if (static_cast(count) != ifaddr_request.header.nlmsg_len) { + close(fd); + return -1; + } + struct ifaddrs* start = NULL; + struct ifaddrs* current = NULL; + char buf[kMaxReadSize]; + ssize_t amount_read = recv(fd, &buf, kMaxReadSize, 0); + while (amount_read > 0) { + nlmsghdr* header = reinterpret_cast(&buf[0]); + size_t header_size = static_cast(amount_read); + for ( ; NLMSG_OK(header, header_size); + header = NLMSG_NEXT(header, header_size)) { + switch (header->nlmsg_type) { + case NLMSG_DONE: + // Success. Return. + *result = start; + close(fd); + return 0; + case NLMSG_ERROR: + close(fd); + freeifaddrs(start); + return -1; + case RTM_NEWADDR: { + ifaddrmsg* address_msg = + reinterpret_cast(NLMSG_DATA(header)); + rtattr* rta = IFA_RTA(address_msg); + ssize_t payload_len = IFA_PAYLOAD(header); + while (RTA_OK(rta, payload_len)) { + if (rta->rta_type == IFA_ADDRESS) { + int family = address_msg->ifa_family; + if (family == AF_INET || family == AF_INET6) { + ifaddrs* newest = new ifaddrs; + memset(newest, 0, sizeof(ifaddrs)); + if (current) { + current->ifa_next = newest; + } else { + start = newest; + } + if (populate_ifaddrs(newest, address_msg, RTA_DATA(rta), + RTA_PAYLOAD(rta)) != 0) { + freeifaddrs(start); + *result = NULL; + return -1; + } + current = newest; + } + } + rta = RTA_NEXT(rta, payload_len); + } + break; + } + } + } + amount_read = recv(fd, &buf, kMaxReadSize, 0); + } + close(fd); + freeifaddrs(start); + return -1; +} +void freeifaddrs(struct ifaddrs* addrs) { + struct ifaddrs* last = NULL; + struct ifaddrs* cursor = addrs; + while (cursor) { + delete[] cursor->ifa_name; + delete cursor->ifa_addr; + delete cursor->ifa_netmask; + last = cursor; + cursor = cursor->ifa_next; + delete last; + } +} diff --git a/platform/android/ifaddrs_android.h b/platform/android/ifaddrs_android.h new file mode 100644 index 000000000000..539fa404551b --- /dev/null +++ b/platform/android/ifaddrs_android.h @@ -0,0 +1,46 @@ +/* + * libjingle + * Copyright 2013, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef TALK_BASE_IFADDRS_ANDROID_H_ +#define TALK_BASE_IFADDRS_ANDROID_H_ +#include +#include +// Implementation of getifaddrs for Android. +// Fills out a list of ifaddr structs (see below) which contain information +// about every network interface available on the host. +// See 'man getifaddrs' on Linux or OS X (nb: it is not a POSIX function). +struct ifaddrs { + struct ifaddrs* ifa_next; + char* ifa_name; + unsigned int ifa_flags; + struct sockaddr* ifa_addr; + struct sockaddr* ifa_netmask; + // Real ifaddrs has broadcast, point to point and data members. + // We don't need them (yet?). +}; +int getifaddrs(struct ifaddrs** result); +void freeifaddrs(struct ifaddrs* addrs); +#endif // TALK_BASE_IFADDRS_ANDROID_H_ diff --git a/platform/android/java/src/com/android/godot/Godot.java b/platform/android/java/src/com/android/godot/Godot.java index 35ecdc818e37..bd973ce49b06 100644 --- a/platform/android/java/src/com/android/godot/Godot.java +++ b/platform/android/java/src/com/android/godot/Godot.java @@ -65,6 +65,9 @@ import java.io.InputStream; public class Godot extends Activity implements SensorEventListener { + + static final int MAX_SINGLETONS = 64; + static public class SingletonBase { protected void registerClass(String p_name, String[] p_methods) { @@ -104,8 +107,21 @@ public class Godot extends Activity implements SensorEventListener } + + Godot.singletons[Godot.singleton_count++]=this; } + protected void onMainActivityResult(int requestCode, int resultCode, Intent data) { + + + } + + protected void onMainResume() { + + + } + + public void registerMethods() {} } @@ -133,6 +149,12 @@ public class Godot extends Activity implements SensorEventListener //setTitle(title); } + + static SingletonBase singletons[] = new SingletonBase[MAX_SINGLETONS]; + static int singleton_count=0; + + + public interface ResultCallback { public void callback(int requestCode, int resultCode, Intent data); }; @@ -147,6 +169,11 @@ public class Godot extends Activity implements SensorEventListener result_callback.callback(requestCode, resultCode, data); result_callback = null; }; + + for(int i=0;itype == KeyPress) { - - // saved the time of the last keyrelease to see - // if it's the same as this keypress. - if (xkeyevent->time==last_keyrelease_time) - echo=true; - } else { - + if (xkeyevent->type != KeyPress) { + // make sure there are events pending, // so this call won't block. if (XPending(x11_display)>0) { @@ -615,17 +607,21 @@ void OS_X11::handle_key_event(XKeyEvent *p_event) { // not very helpful today. ::Time tresh=ABS(peek_event.xkey.time-xkeyevent->time); - if (peek_event.type == KeyPress && tresh<5 ) - echo=true; + if (peek_event.type == KeyPress && tresh<5 ) { + KeySym rk; + nbytes=XLookupString((XKeyEvent*)&peek_event, str, 256, &rk, NULL); + if (rk==keysym_keycode) { + XEvent event; + XNextEvent(x11_display, &event); //erase next event + handle_key_event( (XKeyEvent*)&event,true ); + return; //ignore current, echo next + } + } // use the time from peek_event so it always works - last_keyrelease_time=peek_event.xkey.time; - } else { - last_keyrelease_time=xkeyevent->time; } - // save the time to check for echo when keypress happens - + // save the time to check for echo when keypress happens } @@ -643,7 +639,7 @@ void OS_X11::handle_key_event(XKeyEvent *p_event) { event.key.scancode=keycode; event.key.unicode=unicode; - event.key.echo=echo; + event.key.echo=p_echo; if (event.key.scancode==KEY_BACKTAB) { //make it consistent accross platforms. @@ -1017,7 +1013,7 @@ String OS_X11::get_name() { Error OS_X11::shell_open(String p_uri) { - + return ERR_UNAVAILABLE; } diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index 491b8fa00d63..77ef37f6f4ca 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -90,7 +90,7 @@ class OS_X11 : public OS_Unix { MouseMode mouse_mode; Point2i center; - void handle_key_event(XKeyEvent *p_event); + void handle_key_event(XKeyEvent *p_event,bool p_echo=false); void process_xevents(); virtual void delete_main_loop(); IP_Unix *ip_unix; diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp index a94b2347fc52..dea2411f1d61 100644 --- a/scene/2d/canvas_item.cpp +++ b/scene/2d/canvas_item.cpp @@ -447,7 +447,7 @@ float CanvasItem::get_self_opacity() const { void CanvasItem::set_blend_mode(BlendMode p_blend_mode) { - ERR_FAIL_INDEX(p_blend_mode,4); + ERR_FAIL_INDEX(p_blend_mode,5); blend_mode=p_blend_mode; VisualServer::get_singleton()->canvas_item_set_blend_mode(canvas_item,VS::MaterialBlendMode(blend_mode)); @@ -794,7 +794,7 @@ void CanvasItem::_bind_methods() { ADD_PROPERTY( PropertyInfo(Variant::BOOL,"visibility/behind_parent"), _SCS("set_draw_behind_parent"),_SCS("is_draw_behind_parent_enabled") ); ADD_PROPERTY( PropertyInfo(Variant::BOOL,"visibility/on_top",PROPERTY_HINT_NONE,"",0), _SCS("_set_on_top"),_SCS("_is_on_top") ); //compatibility - ADD_PROPERTYNZ( PropertyInfo(Variant::INT,"visibility/blend_mode",PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul"), _SCS("set_blend_mode"),_SCS("get_blend_mode") ); + ADD_PROPERTYNZ( PropertyInfo(Variant::INT,"visibility/blend_mode",PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul,PMAlpha"), _SCS("set_blend_mode"),_SCS("get_blend_mode") ); //exporting these two things doesn't really make much sense i think //ADD_PROPERTY( PropertyInfo(Variant::BOOL,"transform/toplevel"), _SCS("set_as_toplevel"),_SCS("is_set_as_toplevel") ); //ADD_PROPERTY(PropertyInfo(Variant::BOOL,"transform/notify"),_SCS("set_transform_notify"),_SCS("is_transform_notify_enabled")); @@ -810,6 +810,7 @@ void CanvasItem::_bind_methods() { BIND_CONSTANT( BLEND_MODE_ADD ); BIND_CONSTANT( BLEND_MODE_SUB ); BIND_CONSTANT( BLEND_MODE_MUL ); + BIND_CONSTANT( BLEND_MODE_PREMULT_ALPHA ); BIND_CONSTANT( NOTIFICATION_DRAW); diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h index 1c104c5fc259..604eef052733 100644 --- a/scene/2d/canvas_item.h +++ b/scene/2d/canvas_item.h @@ -49,7 +49,8 @@ public: BLEND_MODE_MIX, //default BLEND_MODE_ADD, BLEND_MODE_SUB, - BLEND_MODE_MUL + BLEND_MODE_MUL, + BLEND_MODE_PREMULT_ALPHA }; private: diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 637a81611201..2ddfa1078b91 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -158,7 +158,7 @@ void Material::_bind_methods() { for(int i=0;i1) { @@ -972,6 +979,11 @@ Error EditorTextureImportPlugin::import2(const String& p_path, const Refleaf) { - +#if 0 if (p_aabb.has_point(p_triangle->vertices[0]) && p_aabb.has_point(p_triangle->vertices[1]) &&p_aabb.has_point(p_triangle->vertices[2])) { //face is completely enclosed, add area p_octant->surface_area+=Face3(p_triangle->vertices[0],p_triangle->vertices[1],p_triangle->vertices[2]).get_area(); @@ -433,12 +435,18 @@ void BakedLightBaker::_octree_insert(const AABB& p_aabb,Octant *p_octant,Triangl p.d=-p_aabb.pos[i]; poly=Geometry::clip_polygon(poly,p); } - //calculate area - for(int i=2;isurface_area+=Face3(poly[0],poly[i-1],poly[i]).get_area(); - } - } + + //calculate area + float clipped_area=0; + for(int i=2;ivertices[0],p_triangle->vertices[1],p_triangle->vertices[2]).get_area())+" clipped: "+rtos(clipped_area)); + p_octant->surface_area+=clipped_area; + } +#endif } else { @@ -500,7 +508,7 @@ void BakedLightBaker::_make_octree() { octree_aabb=base; cell_size=base.size.x; - for(int i=0;i<=octree_depth;i++) + for(int i=0;ir) return; //oh crap! outside radius - float intensity = 1.0 - (d/r)*(d/r); //not gauss but.. + float intensity = 1.0;// - (d/r)*(d/r); //not gauss but.. p_octant->light_accum[0]+=p_light.r*intensity; p_octant->light_accum[1]+=p_light.g*intensity; p_octant->light_accum[2]+=p_light.b*intensity; @@ -558,6 +566,42 @@ void BakedLightBaker::_plot_light(const Vector3& p_plot_pos,const AABB& p_plot_a } } +void BakedLightBaker::_plot_light_point(const Vector3& p_plot_pos, Octant *p_octant, const AABB& p_aabb,const Color& p_light) { + + + if (p_octant->leaf) { + + p_octant->light_accum[0]+=p_light.r; + p_octant->light_accum[1]+=p_light.g; + p_octant->light_accum[2]+=p_light.b; + + } else { + + for(int i=0;i<8;i++) { + + if (!p_octant->children[i]) + continue; + + AABB aabb=p_aabb; + aabb.size*=0.5; + if (i&1) + aabb.pos.x+=aabb.size.x; + if (i&2) + aabb.pos.y+=aabb.size.y; + if (i&4) + aabb.pos.z+=aabb.size.z; + + + if (!aabb.has_point(p_plot_pos)) + continue; + + _plot_light_point(p_plot_pos,p_octant->children[i],aabb,p_light); + + } + + } +} + void BakedLightBaker::_throw_ray(const Vector3& p_begin, const Vector3& p_end,const Color& p_light,float *p_att_curve,float p_att_curve_len,int p_bounces) { @@ -692,6 +736,7 @@ void BakedLightBaker::_throw_ray(const Vector3& p_begin, const Vector3& p_end,co aabb.size=Vector3(2,2,2)*cell_size*plot_size; _plot_light(r_point,aabb,octree,octree_aabb,p_light); +// _plot_light_point(r_point,octree,octree_aabb,p_light); } @@ -772,9 +817,21 @@ void BakedLightBaker::bake(Node* p_node) { +void BakedLightEditor::_end_baking() { + + if (!bake_thread) + return; + + bake_thread_exit=true; + Thread::wait_to_finish(bake_thread); + bake_thread=NULL; + bake_thread_exit=false; +} + void BakedLightEditor::_node_removed(Node *p_node) { if(p_node==node) { + _end_baking(); node=NULL; p_node->remove_child(preview); preview->set_mesh(Ref()); @@ -784,6 +841,79 @@ void BakedLightEditor::_node_removed(Node *p_node) { } +void BakedLightEditor::_bake_thread_func(void *arg) { + + BakedLightEditor *ble = (BakedLightEditor*)arg; + + while(!ble->bake_thread_exit) { + + ble->baker->throw_rays(1000); + } + +} + + + +void BakedLightEditor::_notification(int p_option) { + + + if (p_option==NOTIFICATION_PROCESS) { + + if (bake_thread) { + + update_timeout-=get_process_delta_time(); + if (update_timeout<0) { + + + + float norm = baker->get_normalization(); + float max_lum=0; + { + DVector::Write cw=colors.write(); + BakedLightBaker::Octant *oct = baker->leaf_list; + int vert_idx=0; + + while(oct) { + + Color color; + + + color.r=oct->light_accum[0]/norm; + color.g=oct->light_accum[1]/norm; + color.b=oct->light_accum[2]/norm; + float lum = color.get_v(); + //if (lum<0.05) + // color.a=0; + if (lum>max_lum) + max_lum=lum; + + for (int i=0;i<36;i++) { + + + cw[vert_idx++]=color; + } + + oct=oct->next_leaf; + + } + } + + + Array a; + a.resize(Mesh::ARRAY_MAX); + a[Mesh::ARRAY_VERTEX]=vertices; + a[Mesh::ARRAY_COLOR]=colors; + while(mesh->get_surface_count()) + mesh->surface_remove(0); + mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES,a); + mesh->surface_set_material(0,material); + + update_timeout=1; + } + } + } +} + void BakedLightEditor::_menu_option(int p_option) { @@ -797,13 +927,9 @@ void BakedLightEditor::_menu_option(int p_option) { preview->set_mesh(Ref()); baker->base_inv=node->get_global_transform().affine_inverse(); baker->bake(node); - baker->throw_rays(100000); - float norm = baker->get_normalization(); - float max_lum=0; print_line("CELLS: "+itos(baker->cell_count)); - DVector colors; - DVector vertices; + print_line("cell size: "+rtos(baker->cell_size)); colors.resize(baker->cell_count*36); vertices.resize(baker->cell_count*36); @@ -817,12 +943,6 @@ void BakedLightEditor::_menu_option(int p_option) { while(oct) { Color color; - color.r=oct->light_accum[0]/norm; - color.g=oct->light_accum[1]/norm; - color.b=oct->light_accum[2]/norm; - float lum = color.get_v(); - if (lum>max_lum) - max_lum=lum; for (int i=0;i<6;i++) { @@ -845,7 +965,7 @@ void BakedLightEditor::_menu_option(int p_option) { } for(int j=0;j<4;j++) { - face_points[j]*=baker->cell_size; + face_points[j]*=baker->cell_size*0.5; face_points[j]+=Vector3(oct->offset[0],oct->offset[1],oct->offset[2]); } @@ -873,25 +993,20 @@ void BakedLightEditor::_menu_option(int p_option) { } - print_line("max lum: "+rtos(max_lum)); Array a; a.resize(Mesh::ARRAY_MAX); a[Mesh::ARRAY_VERTEX]=vertices; a[Mesh::ARRAY_COLOR]=colors; + while(mesh->get_surface_count()) + mesh->surface_remove(0); + mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES,a); + mesh->surface_set_material(0,material); - Ref matcol = memnew( FixedMaterial ); - matcol->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY,true); - matcol->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true); - matcol->set_flag(FixedMaterial::FLAG_UNSHADED,true); - matcol->set_flag(FixedMaterial::FLAG_DOUBLE_SIDED,true); - matcol->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1)); - Ref m = memnew( Mesh ); - m->add_surface(Mesh::PRIMITIVE_TRIANGLES,a); - m->surface_set_material(0,matcol); - preview->set_mesh(m); - - - + bake_thread_exit=false; + update_timeout=0; + set_process(true); + bake_thread=Thread::create(_bake_thread_func,this); + preview->set_mesh(mesh); } break; @@ -914,6 +1029,7 @@ void BakedLightEditor::edit(BakedLight *p_baked_light) { } node=p_baked_light; + _end_baking(); if (node) node->add_child(preview); @@ -943,6 +1059,19 @@ BakedLightEditor::BakedLightEditor() { node=NULL; baker = memnew( BakedLightBaker ); preview = memnew( MeshInstance ); + bake_thread=NULL; + update_timeout=0; + + material = Ref ( memnew( FixedMaterial ) ); + material->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY,true); + material->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true); + material->set_flag(FixedMaterial::FLAG_UNSHADED,true); + material->set_flag(FixedMaterial::FLAG_DOUBLE_SIDED,true); + material->set_parameter(FixedMaterial::PARAM_DIFFUSE,Color(1,1,1)); + + mesh = Ref( memnew( Mesh )); + + } BakedLightEditor::~BakedLightEditor() { diff --git a/tools/editor/plugins/baked_light_editor_plugin.h b/tools/editor/plugins/baked_light_editor_plugin.h index 698d3f825fc8..9424503a16b5 100644 --- a/tools/editor/plugins/baked_light_editor_plugin.h +++ b/tools/editor/plugins/baked_light_editor_plugin.h @@ -19,6 +19,15 @@ class BakedLightEditor : public Control { OBJ_TYPE(BakedLightEditor, Control ); + float update_timeout; + DVector colors; + DVector vertices; + Ref mesh; + Ref material; + + Thread *bake_thread; + bool bake_thread_exit; + MeshInstance *preview; BakedLightBaker *baker; AcceptDialog *err_dialog; @@ -32,12 +41,15 @@ class BakedLightEditor : public Control { MENU_OPTION_CLEAR }; + static void _bake_thread_func(void *arg); + void _end_baking(); void _menu_option(int); friend class BakedLightEditorPlugin; protected: void _node_removed(Node *p_node); static void _bind_methods(); + void _notification(int p_what); public: void edit(BakedLight *p_baked_light); diff --git a/tools/editor/plugins/canvas_item_editor_plugin.cpp b/tools/editor/plugins/canvas_item_editor_plugin.cpp index 5f87b791f7de..6540ae9288ac 100644 --- a/tools/editor/plugins/canvas_item_editor_plugin.cpp +++ b/tools/editor/plugins/canvas_item_editor_plugin.cpp @@ -608,7 +608,14 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) { if (b.button_index==BUTTON_WHEEL_DOWN) { + float prev_zoom=zoom; zoom=zoom*0.95; + { + Point2 ofs(b.x,b.y); + ofs = ofs/prev_zoom - ofs/zoom; + h_scroll->set_val( h_scroll->get_val() + ofs.x ); + v_scroll->set_val( v_scroll->get_val() + ofs.y ); + } _update_scroll(0); viewport->update(); return; @@ -616,7 +623,15 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) { if (b.button_index==BUTTON_WHEEL_UP) { + float prev_zoom=zoom; zoom=zoom*(1.0/0.95); + { + Point2 ofs(b.x,b.y); + ofs = ofs/prev_zoom - ofs/zoom; + h_scroll->set_val( h_scroll->get_val() + ofs.x ); + v_scroll->set_val( v_scroll->get_val() + ofs.y ); + } + _update_scroll(0); viewport->update(); return; diff --git a/tools/editor/plugins/script_editor_plugin.cpp b/tools/editor/plugins/script_editor_plugin.cpp index 83cf753692d3..31ccc79d2a9b 100644 --- a/tools/editor/plugins/script_editor_plugin.cpp +++ b/tools/editor/plugins/script_editor_plugin.cpp @@ -687,6 +687,26 @@ void ScriptEditor::_menu_option(int p_option) { current->get_text_edit()->query_code_comple(); + } break; + case EDIT_AUTO_INDENT: { + + TextEdit *te = current->get_text_edit(); + String text = te->get_text(); + Ref