From 930cb97c7d5e1bd4946564c97ea636ca3ed81963 Mon Sep 17 00:00:00 2001 From: firefly2442 Date: Tue, 8 Sep 2015 22:48:22 -0500 Subject: [PATCH 01/38] hide opened menu when click on menu button --- scene/gui/popup_menu.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index e706053592f..5afe2ed7048 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -323,8 +323,10 @@ void PopupMenu::_input_event(const InputEvent &p_event) { invalidated_click=false; break; } - if (over<0 || items[over].separator || items[over].disabled) + if (over<0 || items[over].separator || items[over].disabled) { + hide(); break; //non-activable + } if (items[over].submenu!="") { From 0e60b959cd19e6971ee19851a340c13d28f228c1 Mon Sep 17 00:00:00 2001 From: firefly2442 Date: Wed, 9 Sep 2015 02:55:47 -0500 Subject: [PATCH 02/38] Fix absolute paths used in doxygen, ignore generated documentation in Git --- .gitignore | 5 ++++- Doxyfile | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 613eff442a2..66cf235f67c 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,9 @@ tools/editor/editor_icons.cpp make.bat log.txt +# Doxygen generated documentation +doc/doxygen/* + # Javascript specific *.bc @@ -282,4 +285,4 @@ cscope.in.out cscope.po.out godot.creator.* -projects/ \ No newline at end of file +projects/ diff --git a/Doxyfile b/Doxyfile index c32a1bf1955..4268ed8c7db 100644 --- a/Doxyfile +++ b/Doxyfile @@ -51,14 +51,14 @@ PROJECT_BRIEF = "Game Engine MIT" # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. -PROJECT_LOGO = E:/development/godot/logo_small.png +PROJECT_LOGO = ./logo_small.png # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = E:/development/godot/doxygen +OUTPUT_DIRECTORY = ./doc/doxygen/ # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and @@ -768,7 +768,7 @@ WARN_LOGFILE = # spaces. # Note: If this tag is empty the current directory is searched. -INPUT = E:/development/godot +INPUT = ./core/ ./main/ ./scene/ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses From 867c95223d0e972b88aa26a3d8dc40530b02f09b Mon Sep 17 00:00:00 2001 From: masoud bh Date: Wed, 16 Sep 2015 16:14:38 +0430 Subject: [PATCH 03/38] Fix android build script some fixes for android build script. remove armv6,x86 options and add "android_arch" option for select compiler architecture (armv7,armv6,x86)(default armv7). add architecture suffix for output files and you can compile for several architecture simultaneously. example: libgodot.android.opt.debug.armv7.so libgodot.android.opt.debug.armv7.neon.so libgodot.android.opt.debug.armv6.so libgodot.android.opt.debug.x86.so now we can enable/disable neon on armv7 with "android_neon" option (default enable). add "NDK_TARGET_X86" option for select toolchain to use for the NDK x86 (default x86-4.8). change inputs model for "ndk_platform" option (default android-15). fix armv7 ccflags. with this patch, must put libgodot_android.so file in specific architecture folder: armv7 (default): /libs/armeabi-v7a/ armv6: /libs/armeabi/ x86: /libs/x86/ --- drivers/webp/dsp/dsp.h | 2 +- platform/android/detect.py | 91 ++++++++++++++++++++++++-------------- 2 files changed, 58 insertions(+), 35 deletions(-) diff --git a/drivers/webp/dsp/dsp.h b/drivers/webp/dsp/dsp.h index afe30413c64..fd686a8532b 100644 --- a/drivers/webp/dsp/dsp.h +++ b/drivers/webp/dsp/dsp.h @@ -29,7 +29,7 @@ extern "C" { #define WEBP_USE_SSE2 #endif -#if defined(__ANDROID__) && defined(__ARM_ARCH_7A__) +#if defined(__ANDROID__) && defined(__ARM_ARCH_7A__) && defined(__ARM_NEON__) #define WEBP_ANDROID_NEON // Android targets that might support NEON #endif diff --git a/platform/android/detect.py b/platform/android/detect.py index fce1fe3ed6c..b29f4899cde 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -20,15 +20,14 @@ def can_build(): def get_opts(): return [ - ('ANDROID_NDK_ROOT', 'the path to Android NDK', os.environ.get("ANDROID_NDK_ROOT", 0)), - ('NDK_TOOLCHAIN', 'toolchain to use for the NDK',"arm-eabi-4.4.0"), - #android 2.3 - ('ndk_platform', 'compile for platform: (2.2,2.3)',"2.2"), - ('NDK_TARGET', 'toolchain to use for the NDK',"arm-linux-androideabi-4.8"), - ('android_stl','enable STL support in android port (for modules)','no'), - ('armv6','compile for older phones running arm v6 (instead of v7+neon+smp)','no'), - ('x86','Xompile for Android-x86','no') - + ('ANDROID_NDK_ROOT', 'the path to Android NDK', os.environ.get("ANDROID_NDK_ROOT", 0)), + ('NDK_TOOLCHAIN', 'toolchain to use for the NDK',"arm-eabi-4.4.0"), + ('NDK_TARGET', 'toolchain to use for the NDK',"arm-linux-androideabi-4.8"), + ('NDK_TARGET_X86', 'toolchain to use for the NDK x86',"x86-4.8"), + ('ndk_platform', 'compile for platform: (android- , example: android-15)',"android-15"), + ('android_arch', 'select compiler architecture: (armv7/armv6/x86)',"armv7"), + ('android_neon','enable neon (armv7 only)',"yes"), + ('android_stl','enable STL support in android port (for modules)',"no") ] def get_flags(): @@ -94,8 +93,13 @@ def configure(env): env['SPAWN'] = mySpawn - if env['x86']=='yes': - env['NDK_TARGET']='x86-4.8' + ndk_platform=env['ndk_platform'] + + if env['android_arch'] not in ['armv7','armv6','x86']: + env['android_arch']='armv7' + + if env['android_arch']=='x86': + env['NDK_TARGET']=env['NDK_TARGET_X86'] if env['PLATFORM'] == 'win32': import methods @@ -103,22 +107,28 @@ def configure(env): #env['SPAWN'] = methods.win32_spawn env['SHLIBSUFFIX'] = '.so' -# env.android_source_modules.append("../libs/apk_expansion") + #env.android_source_modules.append("../libs/apk_expansion") env.android_source_modules.append("../libs/google_play_services") env.android_source_modules.append("../libs/downloader_library") env.android_source_modules.append("../libs/play_licensing") - - ndk_platform="" - ndk_platform="android-15" - - print("Godot Android!!!!!") + neon_text="" + if env["android_arch"]=="armv7" and env['android_neon']=='yes': + neon_text=" (with neon)" + print("Godot Android!!!!! ("+env['android_arch']+")"+neon_text) env.Append(CPPPATH=['#platform/android']) - if env['x86']=='yes': - env.extra_suffix=".x86" - + if env['android_arch']=='x86': + env.extra_suffix=".x86"+env.extra_suffix + elif env['android_arch']=='armv6': + env.extra_suffix=".armv6"+env.extra_suffix + elif env["android_arch"]=="armv7": + if env['android_neon']=='yes': + env.extra_suffix=".armv7.neon"+env.extra_suffix + else: + env.extra_suffix=".armv7"+env.extra_suffix + gcc_path=env["ANDROID_NDK_ROOT"]+"/toolchains/"+env["NDK_TARGET"]+"/prebuilt/"; import os @@ -136,7 +146,7 @@ def configure(env): env['ENV']['PATH'] = gcc_path+":"+env['ENV']['PATH'] - if env['x86']=='yes': + if env['android_arch']=='x86': env['CC'] = gcc_path+'/i686-linux-android-gcc' env['CXX'] = gcc_path+'/i686-linux-android-g++' env['AR'] = gcc_path+"/i686-linux-android-ar" @@ -149,7 +159,7 @@ def configure(env): env['RANLIB'] = gcc_path+"/arm-linux-androideabi-ranlib" env['AS'] = gcc_path+"/arm-linux-androideabi-as" - if env['x86']=='yes': + if env['android_arch']=='x86': env['ARCH'] = 'arch-x86' else: env['ARCH'] = 'arch-arm' @@ -163,12 +173,18 @@ def configure(env): env.Append(CPPPATH=[gcc_include]) # env['CCFLAGS'] = string.split('-DNO_THREADS -MMD -MP -MF -fpic -ffunction-sections -funwind-tables -fstack-protector -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -Wno-psabi -march=armv5te -mtune=xscale -msoft-float -fno-exceptions -mthumb -fno-strict-aliasing -DANDROID -Wa,--noexecstack -DGLES2_ENABLED ') - if env['x86']=='yes': + env['neon_enabled']=False + if env['android_arch']=='x86': env['CCFLAGS'] = string.split('-DNO_STATVFS -MMD -MP -MF -fpic -ffunction-sections -funwind-tables -fstack-protector -fvisibility=hidden -D__GLIBC__ -Wno-psabi -ftree-vectorize -funsafe-math-optimizations -fno-strict-aliasing -DANDROID -Wa,--noexecstack -DGLES2_ENABLED') - elif env["armv6"]!="no": + elif env["android_arch"]=="armv6": env['CCFLAGS'] = string.split('-DNO_STATVFS -MMD -MP -MF -fpic -ffunction-sections -funwind-tables -fstack-protector -fvisibility=hidden -D__ARM_ARCH_6__ -D__GLIBC__ -Wno-psabi -march=armv6 -mfpu=vfp -mfloat-abi=softfp -funsafe-math-optimizations -fno-strict-aliasing -DANDROID -Wa,--noexecstack -DGLES2_ENABLED') - else: - env['CCFLAGS'] = string.split('-DNO_STATVFS -MMD -MP -MF -fpic -ffunction-sections -funwind-tables -fstack-protector -fvisibility=hidden -D__ARM_ARCH_7__ -D__GLIBC__ -Wno-psabi -march=armv6 -mfpu=neon -mfloat-abi=softfp -ftree-vectorize -funsafe-math-optimizations -fno-strict-aliasing -DANDROID -Wa,--noexecstack -DGLES2_ENABLED') + elif env["android_arch"]=="armv7": + env['CCFLAGS'] = string.split('-DNO_STATVFS -MMD -MP -MF -fpic -ffunction-sections -funwind-tables -fstack-protector -fvisibility=hidden -D__ARM_ARCH_7__ -D__ARM_ARCH_7A__ -D__GLIBC__ -Wno-psabi -march=armv7-a -mfloat-abi=softfp -ftree-vectorize -funsafe-math-optimizations -fno-strict-aliasing -DANDROID -Wa,--noexecstack -DGLES2_ENABLED') + if env['android_neon']=='yes': + env['neon_enabled']=True + env.Append(CCFLAGS=['-mfpu=neon','-D__ARM_NEON__']) + else: + env.Append(CCFLAGS=['-mfpu=vfpv3-d16']) env.Append(LDPATH=[ld_path]) env.Append(LIBS=['OpenSLES']) @@ -192,17 +208,22 @@ def configure(env): env.Append(CCFLAGS=['-D_DEBUG', '-g1', '-Wall', '-O0', '-DDEBUG_ENABLED']) env.Append(CPPFLAGS=['-DDEBUG_MEMORY_ALLOC']) - if env["armv6"] == "no" and env['x86'] != 'yes': - env['neon_enabled']=True - env.Append(CPPFLAGS=['-DANDROID_ENABLED', '-DUNIX_ENABLED', '-DNO_FCNTL','-DMPC_FIXED_POINT']) # env.Append(CPPFLAGS=['-DANDROID_ENABLED', '-DUNIX_ENABLED','-DMPC_FIXED_POINT']) if (env['android_stl']=='yes'): #env.Append(CCFLAGS=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/system/include"]) - env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.4.3/include"]) - env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.4.3/libs/armeabi/include"]) - env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.4.3/libs/armeabi"]) + env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.8/include"]) + if env['android_arch']=='x86': + env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.8/libs/x86/include"]) + env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.8/libs/x86"]) + elif env['android_arch']=='armv6': + env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi/include"]) + env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi"]) + elif env["android_arch"]=="armv7": + env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi-v7a/include"]) + env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi-v7a"]) + env.Append(LIBS=["gnustl_static","supc++"]) env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cpufeatures"]) @@ -213,10 +234,12 @@ def configure(env): env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gabi++/include"]) env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cpufeatures"]) - if env['x86']=='yes': + if env['android_arch']=='x86': env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gabi++/libs/x86"]) - else: + elif env["android_arch"]=="armv6": env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gabi++/libs/armeabi"]) + elif env["android_arch"]=="armv7": + env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"]+"/sources/cxx-stl/gabi++/libs/armeabi-v7a"]) env.Append(LIBS=['gabi++_static']) env.Append(CCFLAGS=["-fno-exceptions",'-DNO_SAFE_CAST']) From 9d540afd1cd3eeb7cc318d2761bae778d32218be Mon Sep 17 00:00:00 2001 From: masoud bh Date: Wed, 16 Sep 2015 16:28:01 +0430 Subject: [PATCH 04/38] android remove "gen" folder from git. --- .../expansion/downloader/BuildConfig.java | 6 -- .../vending/expansion/downloader/R.java | 73 ------------------- 2 files changed, 79 deletions(-) delete mode 100644 platform/android/libs/downloader_library/gen/com/android/vending/expansion/downloader/BuildConfig.java delete mode 100644 platform/android/libs/downloader_library/gen/com/android/vending/expansion/downloader/R.java diff --git a/platform/android/libs/downloader_library/gen/com/android/vending/expansion/downloader/BuildConfig.java b/platform/android/libs/downloader_library/gen/com/android/vending/expansion/downloader/BuildConfig.java deleted file mode 100644 index da9d06e63cb..00000000000 --- a/platform/android/libs/downloader_library/gen/com/android/vending/expansion/downloader/BuildConfig.java +++ /dev/null @@ -1,6 +0,0 @@ -/** Automatically generated file. DO NOT MODIFY */ -package com.android.vending.expansion.downloader; - -public final class BuildConfig { - public final static boolean DEBUG = false; -} \ No newline at end of file diff --git a/platform/android/libs/downloader_library/gen/com/android/vending/expansion/downloader/R.java b/platform/android/libs/downloader_library/gen/com/android/vending/expansion/downloader/R.java deleted file mode 100644 index 330aed18569..00000000000 --- a/platform/android/libs/downloader_library/gen/com/android/vending/expansion/downloader/R.java +++ /dev/null @@ -1,73 +0,0 @@ -/* AUTO-GENERATED FILE. DO NOT MODIFY. - * - * This class was automatically generated by the - * aapt tool from the resource data it found. It - * should not be modified by hand. - */ - -package com.android.vending.expansion.downloader; - -public final class R { - public static final class attr { - } - public static final class drawable { - public static int notify_panel_notification_icon_bg=0x7f020000; - } - public static final class id { - public static int appIcon=0x7f060001; - public static int description=0x7f060007; - public static int notificationLayout=0x7f060000; - public static int progress_bar=0x7f060006; - public static int progress_bar_frame=0x7f060005; - public static int progress_text=0x7f060002; - public static int time_remaining=0x7f060004; - public static int title=0x7f060003; - } - public static final class layout { - public static int status_bar_ongoing_event_progress_bar=0x7f030000; - } - public static final class string { - public static int kilobytes_per_second=0x7f040014; - /** When a download completes, a notification is displayed, and this - string is used to indicate that the download successfully completed. - Note that such a download could have been initiated by a variety of - applications, including (but not limited to) the browser, an email - application, a content marketplace. - */ - public static int notification_download_complete=0x7f040000; - /** When a download completes, a notification is displayed, and this - string is used to indicate that the download failed. - Note that such a download could have been initiated by a variety of - applications, including (but not limited to) the browser, an email - application, a content marketplace. - */ - public static int notification_download_failed=0x7f040001; - public static int state_completed=0x7f040007; - public static int state_connecting=0x7f040005; - public static int state_downloading=0x7f040006; - public static int state_failed=0x7f040013; - public static int state_failed_cancelled=0x7f040012; - public static int state_failed_fetching_url=0x7f040010; - public static int state_failed_sdcard_full=0x7f040011; - public static int state_failed_unlicensed=0x7f04000f; - public static int state_fetching_url=0x7f040004; - public static int state_idle=0x7f040003; - public static int state_paused_by_request=0x7f04000a; - public static int state_paused_network_setup_failure=0x7f040009; - public static int state_paused_network_unavailable=0x7f040008; - public static int state_paused_roaming=0x7f04000d; - public static int state_paused_sdcard_unavailable=0x7f04000e; - public static int state_paused_wifi_disabled=0x7f04000c; - public static int state_paused_wifi_unavailable=0x7f04000b; - public static int state_unknown=0x7f040002; - public static int time_remaining=0x7f040015; - public static int time_remaining_notification=0x7f040016; - } - public static final class style { - public static int ButtonBackground=0x7f050003; - public static int NotificationText=0x7f050000; - public static int NotificationTextSecondary=0x7f050004; - public static int NotificationTextShadow=0x7f050001; - public static int NotificationTitle=0x7f050002; - } -} From afbb6c064c88d743d8a7d04b5dbfb4b0b1b2db7f Mon Sep 17 00:00:00 2001 From: firefly2442 Date: Wed, 16 Sep 2015 15:35:30 -0500 Subject: [PATCH 05/38] ran cppcheck, found unused variables --- core/io/marshalls.cpp | 3 --- core/io/resource_loader.cpp | 2 -- platform/android/audio_driver_opensl.cpp | 3 +-- platform/android/cpu-features.c | 2 +- platform/bb10/audio_driver_bb10.cpp | 1 - servers/physics/body_pair_sw.cpp | 1 - servers/visual/visual_server_raster.cpp | 1 - tools/editor/io_plugins/editor_sample_import_plugin.cpp | 2 +- 8 files changed, 3 insertions(+), 12 deletions(-) diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index b0d24abfe3b..1e76e2b4b24 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -299,10 +299,8 @@ Error decode_variant(Variant& r_variant,const uint8_t *p_buffer, int p_len,int * ERR_FAIL_COND_V(len<12,ERR_INVALID_DATA); Vector names; Vector subnames; - bool absolute; StringName prop; - int i=0; uint32_t namecount=strlen&=0x7FFFFFFF; uint32_t subnamecount = decode_uint32(buf+4); uint32_t flags = decode_uint32(buf+8); @@ -391,7 +389,6 @@ Error decode_variant(Variant& r_variant,const uint8_t *p_buffer, int p_len,int * ie.type=decode_uint32(&buf[0]); ie.device=decode_uint32(&buf[4]); - uint32_t len = decode_uint32(&buf[8])-12; if (r_len) (*r_len)+=12; diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 1e014480f4d..3862790b02c 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -231,14 +231,12 @@ Ref ResourceLoader::load_import_metadata(const String &p local_path = Globals::get_singleton()->localize_path(p_path); String extension=p_path.extension(); - bool found=false; Ref ret; for (int i=0;irecognize(extension)) continue; - found=true; Error err = loader[i]->load_import_metadata(local_path,ret); if (err==OK) diff --git a/platform/android/audio_driver_opensl.cpp b/platform/android/audio_driver_opensl.cpp index a08bc8943cb..761bef27aa7 100644 --- a/platform/android/audio_driver_opensl.cpp +++ b/platform/android/audio_driver_opensl.cpp @@ -236,13 +236,12 @@ void AudioDriverOpenSL::start(){ ERR_FAIL_COND( res !=SL_RESULT_SUCCESS ); /* Initialize arrays required[] and iidArray[] */ - int i; SLboolean required[MAX_NUMBER_INTERFACES]; SLInterfaceID iidArray[MAX_NUMBER_INTERFACES]; #if 0 - for (i=0; iget_shape(p_shape_A)->get_support(p_xform_A.basis.xform(mnormal).normalized()); Vector3 from = p_xform_A.xform(s); Vector3 to = from + motion; diff --git a/servers/visual/visual_server_raster.cpp b/servers/visual/visual_server_raster.cpp index 155d10d85a0..3ae7cfeaf85 100644 --- a/servers/visual/visual_server_raster.cpp +++ b/servers/visual/visual_server_raster.cpp @@ -5211,7 +5211,6 @@ void VisualServerRaster::_light_instance_update_lispsm_shadow(Instance *p_light, AABB proj_space_aabb; - float max_d,min_d; { diff --git a/tools/editor/io_plugins/editor_sample_import_plugin.cpp b/tools/editor/io_plugins/editor_sample_import_plugin.cpp index 9491f957c39..9298b35b3ba 100644 --- a/tools/editor/io_plugins/editor_sample_import_plugin.cpp +++ b/tools/editor/io_plugins/editor_sample_import_plugin.cpp @@ -710,7 +710,7 @@ void EditorSampleImportPlugin::_compress_ima_adpcm(const Vector& p_data,D *(out++) =0; for (i=0;i Date: Wed, 16 Sep 2015 16:43:00 -0500 Subject: [PATCH 06/38] add missing physics unit test to --help listing in main.cpp --- bin/tests/test_main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/tests/test_main.cpp b/bin/tests/test_main.cpp index 3dba347e39b..5567145aa04 100644 --- a/bin/tests/test_main.cpp +++ b/bin/tests/test_main.cpp @@ -61,6 +61,7 @@ const char ** tests_get_names() { "gui", "io", "shaderlang", + "physics", NULL }; From 3c466afb68eeca60f09950680d7eec58471b6882 Mon Sep 17 00:00:00 2001 From: masoud bh Date: Thu, 17 Sep 2015 05:19:43 +0430 Subject: [PATCH 07/38] Fix X11 Editor Boot Splash --- platform/x11/os_x11.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 51f4392eb4f..ef808c54fb6 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -892,7 +892,13 @@ void OS_X11::set_window_maximized(bool p_enabled) { XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + XWindowAttributes xwa; + XGetWindowAttributes(x11_display,DefaultRootWindow(x11_display),&xwa); + current_videomode.width = xwa.width; + current_videomode.height = xwa.height; + maximized = p_enabled; + visual_server->init(); } bool OS_X11::is_window_maximized() const { From 4210d5e4594fcf80bc121bccc916d411663d02bc Mon Sep 17 00:00:00 2001 From: Sergey Lapin Date: Fri, 18 Sep 2015 12:52:34 +0300 Subject: [PATCH 08/38] Remove of use_exclude_ctrl_bones option use_exclude_ctrl_bones option is not implemented. This option is supposed to filter-out unneeded skeleton bones. This option was never implemented (in repository) and confuses users that feature exists. This commit removes this option as this option implementation is not happenning any time soon and claim it exists confuses users into thinking the feature is supported. Closes #2476 --- tools/export/blender25/io_scene_dae/__init__.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tools/export/blender25/io_scene_dae/__init__.py b/tools/export/blender25/io_scene_dae/__init__.py index 5b561673c5b..182ec21e639 100644 --- a/tools/export/blender25/io_scene_dae/__init__.py +++ b/tools/export/blender25/io_scene_dae/__init__.py @@ -104,11 +104,6 @@ class ExportDAE(bpy.types.Operator, ExportHelper): description="Export only objects on the active layers.", default=True, ) - use_exclude_ctrl_bones = BoolProperty( - name="Exclude Control Bones", - description="Exclude skeleton bones with names that begin with 'ctrl'.", - default=True, - ) use_anim = BoolProperty( name="Export Animation", description="Export keyframe animation", From 1d45c9a04c4c755843a162b872e59680fcb9c94d Mon Sep 17 00:00:00 2001 From: George Marques Date: Sun, 20 Sep 2015 16:17:30 -0300 Subject: [PATCH 09/38] Make dict2inst set internal members of instance Fix #2490 --- modules/gdscript/gd_functions.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/gdscript/gd_functions.cpp b/modules/gdscript/gd_functions.cpp index 37ddb2bc411..6f51ac53124 100644 --- a/modules/gdscript/gd_functions.cpp +++ b/modules/gdscript/gd_functions.cpp @@ -904,6 +904,15 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va r_ret = gdscr->_new(NULL,0,r_error); + GDInstance *ins = static_cast(static_cast(r_ret)->get_script_instance()); + Ref gd_ref = ins->get_script(); + + for(Map::Element *E = gd_ref->member_indices.front(); E; E = E->next()) { + if(d.has(E->key())) { + ins->members[E->get().index] = d[E->key()]; + } + } + } break; case HASH: { From d9583f8a7283bbeac1dceaf0885bba297961432e Mon Sep 17 00:00:00 2001 From: Bojidar Marinov Date: Thu, 24 Sep 2015 10:17:06 +0300 Subject: [PATCH 10/38] Add missing \n to world_wrap. Close #2516 The issue was that world_wrap would skip over newlines, without adding them to the output. --- core/ustring.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/ustring.cpp b/core/ustring.cpp index e5419effcb9..7582376fe0b 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -3066,7 +3066,7 @@ String String::world_wrap(int p_chars_per_line) const { } else if (operator[](i)==' ' || operator[](i)=='\t') { last_space=i; } else if (operator[](i)=='\n') { - ret+=substr(from,i-from); + ret+=substr(from,i-from)+"\n"; from=i+1; last_space=-1; } From 2a90186a8e07ef280e438788802a01c5976e57fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20P=C3=A9rez?= Date: Thu, 24 Sep 2015 12:03:06 +0200 Subject: [PATCH 11/38] Disable filters for tilemap in 2D Platformer demo Fixes #2452 --- demos/2d/platformer/tiles_demo.png.flags | 1 + 1 file changed, 1 insertion(+) create mode 100644 demos/2d/platformer/tiles_demo.png.flags diff --git a/demos/2d/platformer/tiles_demo.png.flags b/demos/2d/platformer/tiles_demo.png.flags new file mode 100644 index 00000000000..efb2b8ce5f2 --- /dev/null +++ b/demos/2d/platformer/tiles_demo.png.flags @@ -0,0 +1 @@ +filter=false From 721d9a58c79fc6c82b6cbe27707ed10fb25ad9ba Mon Sep 17 00:00:00 2001 From: Bojidar Marinov Date: Fri, 25 Sep 2015 18:41:42 +0300 Subject: [PATCH 12/38] Removed a badly listed parameter from funcRef::call_func... --- core/func_ref.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/core/func_ref.cpp b/core/func_ref.cpp index 0e43112de88..fe09ae01af0 100644 --- a/core/func_ref.cpp +++ b/core/func_ref.cpp @@ -32,7 +32,6 @@ void FuncRef::_bind_methods() { { MethodInfo mi; mi.name="call"; - mi.arguments.push_back( PropertyInfo( Variant::STRING, "method")); Vector defargs; for(int i=0;i<10;i++) { mi.arguments.push_back( PropertyInfo( Variant::NIL, "arg"+itos(i))); From a0dffc2942c5893e0204cc8d2b85c860c58ef2fe Mon Sep 17 00:00:00 2001 From: Bojidar Marinov Date: Fri, 25 Sep 2015 18:51:04 +0300 Subject: [PATCH 13/38] Update mi.name --- core/func_ref.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/func_ref.cpp b/core/func_ref.cpp index fe09ae01af0..66962710bd7 100644 --- a/core/func_ref.cpp +++ b/core/func_ref.cpp @@ -31,7 +31,7 @@ void FuncRef::_bind_methods() { { MethodInfo mi; - mi.name="call"; + mi.name="call_func"; Vector defargs; for(int i=0;i<10;i++) { mi.arguments.push_back( PropertyInfo( Variant::NIL, "arg"+itos(i))); From f95f099eb2337c9e367bb46f61324b709c42973e Mon Sep 17 00:00:00 2001 From: Jorge Araya Navarro Date: Sat, 26 Sep 2015 23:55:00 -0600 Subject: [PATCH 14/38] Adding the new Python file This script works different compared to the other, for instance, it requires Python 3.x to run. It also have more sophisticate flags to run. And finally this file conforms to PEP8. This script is not finished yet, though. --- tools/docdump/makedocs.py | 184 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 tools/docdump/makedocs.py diff --git a/tools/docdump/makedocs.py b/tools/docdump/makedocs.py new file mode 100644 index 00000000000..d11515e94ad --- /dev/null +++ b/tools/docdump/makedocs.py @@ -0,0 +1,184 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +# +# makedocs.py: Generate documentation for Open Project Wiki +# +# Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. +# +# Contributor: Jorge Araya Navarro +# + +import re +import argparse +import logging +from os import path +from itertools import izip_longest +from xml.etree import ElementTree + +logging.basicConfig(level=logging.INFO) + + +def getxmlfloc(): + """ Returns the supposed location of the XML file + """ + filepath = path.dirname(path.abspath(__file__)) + return path.join(filepath, "class_list.xml") + + +def sortkey(c): + """ Symbols are first, letters second + """ + if "_" == c.attrib["name"][0]: + return True + else: + return c.attrib["name"] + + +def toOP(text): + """ Convert commands in text to Open Project commands + """ + # We are going to do something very complicated with all HTML commands + # sadly, some commands are embedded inside other commands, so some steps + # are needed before converting some commands to Textile markup + groups = re.finditer((r'\[html (?P/?\w+/?)(\]| |=)?(\]| |=)?(?P\w+)?(\]| |=)?(?P"[^"]+")?/?\]'), text) + alignstr = "" + for group in groups: + gd = group.groupdict() + if gd["command"] == "br/": + text = text.replace(group.group(0), "\n\n", 1) + elif gd["command"] == "div": + if gd["value"] == '"center"': + alignstr = "{display:block; margin-left:auto; margin-right:auto;}" + elif gd["value"] == '"left"': + alignstr = "<" + elif gd["value"] == '"right"': + alignstr = ">" + text = text.replace(group.group(0), "\n\n", 1) + elif gd["command"] == "/div": + alignstr = "" + text = text.replace(group.group(0), "\n\n", 1) + elif gd["command"] == "img": + text = text.replace(group.group(0), "!{align}{src}!".format( + align=alignstr, src=gd["value"].strip('"')), 1) + elif gd["command"] == "b" or gd["command"] == "/b": + text = text.replace(group.group(0), "*", 1) + elif gd["command"] == "i" or gd["command"] == "/i": + text = text.replace(group.group(0), "_", 1) + elif gd["command"] == "u" or gd["command"] == "/u": + text = text.replace(group.group(0), "+", 1) + # TODO: Process other non-html commands + return text + "\n\n" + +desc = "Generates documentation from a XML file to different markup languages" + +parser = argparse.ArgumentParser(description=desc) +parser.add_argument("--input", dest="xmlfp", default=getxmlfloc(), + help="Input XML file, default: {}".format(getxmlfloc())) +parser.add_argument("--output-dir", dest="outputdir", required=True, + help="Output directory for generated files") +# TODO: add an option for outputting different markup formats + +args = parser.parse_args() +# Let's check if the file and output directory exists +if not path.isfile(args.xmlfp): + logging.critical("File not found: {}".format(args.xmlfp)) + exit(1) +elif not path.isdir(args.outputdir): + logging.critical("Path does not exist: {}".format(args.outputdir)) + exit(1) + +# Let's begin +tree = ElementTree.parse(args.xmlfp) +root = tree.getroot() + +# Check version attribute exists in +if "version" not in root.attrib: + logging.critical("'s version attribute missing") + exit(1) + +version = root.attrib["version"] +classes = sorted(root, key=sortkey) +# first column is always longer, second column of classes should be shorter +zclasses = izip_longest(classes[:len(classes) / 2 + 1], + classes[len(classes) / 2 + 1:], + fillvalue="") + +# We write the class_list file and also each class file at once +with open(path.join(args.outputdir, "class_list.txt"), "wb") as fcl: + # Write header of table + fcl.write("|^.\n") + fcl.write("|_. Index symbol |_. Class name " + "|_. Index symbol |_. Class name |\n") + fcl.write("|-.\n") + + indexletterl = "" + indexletterr = "" + for gdclassl, gdclassr in zclasses: + # write a row # + # write the index symbol column, left + if indexletterl != gdclassl.attrib["name"][0]: + indexletterl = gdclassl.attrib["name"][0] + fcl.write("| *{}* |".format(indexletterl.upper())) + else: + # empty cell + fcl.write("| |") + # write the class name column, left + fcl.write("\"{name}({title})\":/class_{link}".format( + name=gdclassl.attrib["name"], + title="Go to page of class " + gdclassl.attrib["name"], + link="class_" + gdclassl.attrib["name"].lower())) + + # write the index symbol column, right + if isinstance(gdclassr, ElementTree.Element): + if indexletterr != gdclassr.attrib["name"][0]: + indexletterr = gdclassr.attrib["name"][0] + fcl.write("| *{}* |".format(indexletterr.upper())) + else: + # empty cell + fcl.write("| |") + # We are dealing with an empty string + else: + # two empty cell + fcl.write("| | |\n") + # We won't get the name of the class since there is no ElementTree + # object for the right side of the tuple, so we iterate the next + # tuple instead + continue + + # write the class name column (if any), right + fcl.write("\"{name}({title})\":/{link} |\n".format( + name=gdclassr.attrib["name"], + title="Go to page of class " + gdclassr.attrib["name"], + link=gdclassr.attrib["name"].lower())) + + # row written # + # now, let's write each class page for each class + for gdclass in [gdclassl, gdclassr]: + if not isinstance(ElementTree.Element, gdclass): + continue + + classname = gdclass.attrib["name"] + with open(path.join(args.outputdir, "{}.txt".format( + classname.lower())), "wb") as clsf: + # First level header with the name of the class + clsf.write("h1. {}\n".format(classname)) + # lay the attributes + if "inherits" in gdclass.attrib: + inh = gdclass.attrib["inherits"].strip() + clsf.write( + "*Inherits:* \"{name}({title})\":/class_{link}\n". + format( + name=classname, + title="Go to page of class " + classname, + link=classname.lower())) + if "category" in gdclass.attrib: + clsf.write("*Category:* {}". + format(gdclass.attrib["category"].strip())) + # lay child nodes + for gdchild in gdclass.iter(): + if gdchild.tag == "brief_description": + clsf.write("h2. Brief Description\n") + clsf.write(toOP(gdchild.text.strip())) + # convert commands in text From 7a008afc67cbbdc26e38477621856c04193280b6 Mon Sep 17 00:00:00 2001 From: Jorge Araya Navarro Date: Sun, 27 Sep 2015 19:25:13 -0600 Subject: [PATCH 15/38] XML to Open Project Wiki implemented --- tools/docdump/makedocs.py | 264 +++++++++++++++++++++++++++++++++----- 1 file changed, 229 insertions(+), 35 deletions(-) diff --git a/tools/docdump/makedocs.py b/tools/docdump/makedocs.py index d11515e94ad..81469eed9c6 100644 --- a/tools/docdump/makedocs.py +++ b/tools/docdump/makedocs.py @@ -9,16 +9,47 @@ # Contributor: Jorge Araya Navarro # +# IMPORTANT NOTICE: +# If you are going to modify anything from this file, please be sure to follow +# the Style Guide for Python Code or often called "PEP8". To do this +# automagically just install autopep8: +# +# $ sudo pip3 install autopep8 +# +# and run: +# +# $ autopep8 makedocs.py +# +# Before committing your changes. Also be sure to delete any trailing +# whitespace you may left. +# +# TODO: +# * Refactor code. Refactor strings. +# * Adapt this script for generating content in other markup formats like +# DokuWiki, Markdown, etc. +# * Because people will translate class_list.xml, we should implement +# internalization. +# +# Also check other TODO entries in this script for more information on what is +# left to do. + import re import argparse import logging from os import path -from itertools import izip_longest +from itertools import zip_longest from xml.etree import ElementTree +# add an option to change the verbosity logging.basicConfig(level=logging.INFO) +def tb(string): + """ Return a byte representation of a string + """ + return bytes(string, "UTF-8") + + def getxmlfloc(): """ Returns the supposed location of the XML file """ @@ -30,7 +61,7 @@ def sortkey(c): """ Symbols are first, letters second """ if "_" == c.attrib["name"][0]: - return True + return "A" else: return c.attrib["name"] @@ -50,7 +81,8 @@ def toOP(text): text = text.replace(group.group(0), "\n\n", 1) elif gd["command"] == "div": if gd["value"] == '"center"': - alignstr = "{display:block; margin-left:auto; margin-right:auto;}" + alignstr = ("{display:block; margin-left:auto;" + " margin-right:auto;}") elif gd["value"] == '"left"': alignstr = "<" elif gd["value"] == '"right"': @@ -68,9 +100,115 @@ def toOP(text): text = text.replace(group.group(0), "_", 1) elif gd["command"] == "u" or gd["command"] == "/u": text = text.replace(group.group(0), "+", 1) - # TODO: Process other non-html commands + # Process other non-html commands + groups = re.finditer((r'\[method ((?P[aA0-zZ9_]+)(?:\.))' + r'?(?P[aA0-zZ9_]+)\]'), text) + for group in groups: + gd = group.groupdict() + if gd["class"]: + replacewith = ("\"{gclass}.{method}(Go " + "to page {gclass}, section {method})\"" + ":/class_{lkclass}#{lkmethod}". + format(gclass=gd["class"], + method=gd["method"], + lkclass=gd["class"].lower(), + lkmethod=gd["method"].lower())) + else: + # The method is located in the same wiki page + replacewith = ("\"{method}(Jump to method" + " {method})\":#{lkmethod}". + format(method=gd["method"], + lkmethod=gd["method"].lower())) + + text = text.replace(group.group(0), replacewith, 1) + # Finally, [Classes] are around brackets, make them direct links + groups = re.finditer(r'\[(?P[az0-AZ0_]+)\]', text) + for group in groups: + gd = group.groupdict() + replacewith = ("\"{gclass}(Go to page of class" + " {gclass})\":/class_{lkclass}". + format(gclass=gd["class"], + lkclass=gd["class"].lower())) + text = text.replace(group.group(0), replacewith, 1) + return text + "\n\n" + +def mkfn(node, is_signal=False): + """ Return a string containing a unsorted item for a function + """ + finalstr = "" + name = node.attrib["name"] + rtype = node.find("return") + if rtype: + rtype = rtype.attrib["type"] + else: + rtype = "void" + # write the return type and the function name first + finalstr += "* " + # return type + if not is_signal: + if rtype != "void": + finalstr += " \"{rtype}({title})\":/class_{link} ".format( + rtype=rtype, + title="Go to page of class " + rtype, + link=rtype.lower()) + else: + finalstr += " void " + + # function name + if not is_signal: + finalstr += "\"*{funcname}*({title})\":#{link} ( ".format( + funcname=name, + title="Jump to description for node " + name, + link=name.lower()) + else: + # Signals have no description + finalstr += "*{funcname}* (".format(funcname=name) + # loop for the arguments of the function, if any + args = [] + for arg in sorted( + node.iter(tag="argument"), + key=lambda a: int(a.attrib["index"])): + + twd = "{type} {name}={default}" + tnd = "{type} {name}" + tlk = ("\"*{cls}*(Go to page of class {cls})" + "\":/class_{lcls}") + ntype = arg.attrib["type"] + nname = arg.attrib["name"] + + if "default" in arg.attrib: + args.insert( + -1, + twd.format( + type=tlk.format( + cls=ntype, + lcls=ntype.lower()), + name=nname, + default=arg.attrib["default"])) + else: + # No default value present + args.insert( + -1, + tnd.format( + type=tlk.format( + cls=ntype, + lcls=ntype.lower()), + name=nname)) + # join the arguments together + finalstr += ", ".join(args) + # and, close the function with a ) + finalstr += " )" + # write the qualifier, if any + if "qualifiers" in node.attrib: + qualifier = node.attrib["qualifiers"] + finalstr += " " + qualifier + + finalstr += "\n" + + return finalstr + desc = "Generates documentation from a XML file to different markup languages" parser = argparse.ArgumentParser(description=desc) @@ -100,18 +238,20 @@ if "version" not in root.attrib: version = root.attrib["version"] classes = sorted(root, key=sortkey) +logging.debug("Number of classes: {}".format(len(classes))) +logging.debug("len(classes) / 2 + 1: {}".format(int(len(classes) / 2 + 1))) # first column is always longer, second column of classes should be shorter -zclasses = izip_longest(classes[:len(classes) / 2 + 1], - classes[len(classes) / 2 + 1:], - fillvalue="") +zclasses = zip_longest(classes[:int(len(classes) / 2 + 1)], + classes[int(len(classes) / 2 + 1):], + fillvalue="") # We write the class_list file and also each class file at once with open(path.join(args.outputdir, "class_list.txt"), "wb") as fcl: # Write header of table - fcl.write("|^.\n") - fcl.write("|_. Index symbol |_. Class name " - "|_. Index symbol |_. Class name |\n") - fcl.write("|-.\n") + fcl.write(tb("|^.\n")) + fcl.write(tb("|_. Index symbol |_. Class name " + "|_. Index symbol |_. Class name |\n")) + fcl.write(tb("|-.\n")) indexletterl = "" indexletterr = "" @@ -120,65 +260,119 @@ with open(path.join(args.outputdir, "class_list.txt"), "wb") as fcl: # write the index symbol column, left if indexletterl != gdclassl.attrib["name"][0]: indexletterl = gdclassl.attrib["name"][0] - fcl.write("| *{}* |".format(indexletterl.upper())) + fcl.write(tb("| *{}* |".format(indexletterl.upper()))) else: # empty cell - fcl.write("| |") + fcl.write(tb("| |")) # write the class name column, left - fcl.write("\"{name}({title})\":/class_{link}".format( + fcl.write(tb("\"{name}({title})\":/class_{link}".format( name=gdclassl.attrib["name"], title="Go to page of class " + gdclassl.attrib["name"], - link="class_" + gdclassl.attrib["name"].lower())) + link="class_" + gdclassl.attrib["name"].lower()))) # write the index symbol column, right if isinstance(gdclassr, ElementTree.Element): if indexletterr != gdclassr.attrib["name"][0]: indexletterr = gdclassr.attrib["name"][0] - fcl.write("| *{}* |".format(indexletterr.upper())) + fcl.write(tb("| *{}* |".format(indexletterr.upper()))) else: # empty cell - fcl.write("| |") + fcl.write(tb("| |")) # We are dealing with an empty string else: # two empty cell - fcl.write("| | |\n") + fcl.write(tb("| | |\n")) # We won't get the name of the class since there is no ElementTree # object for the right side of the tuple, so we iterate the next # tuple instead continue # write the class name column (if any), right - fcl.write("\"{name}({title})\":/{link} |\n".format( + fcl.write(tb("\"{name}({title})\":/{link} |\n".format( name=gdclassr.attrib["name"], title="Go to page of class " + gdclassr.attrib["name"], - link=gdclassr.attrib["name"].lower())) + link=gdclassr.attrib["name"].lower()))) # row written # # now, let's write each class page for each class for gdclass in [gdclassl, gdclassr]: - if not isinstance(ElementTree.Element, gdclass): + if not isinstance(gdclass, ElementTree.Element): continue classname = gdclass.attrib["name"] with open(path.join(args.outputdir, "{}.txt".format( classname.lower())), "wb") as clsf: # First level header with the name of the class - clsf.write("h1. {}\n".format(classname)) + clsf.write(tb("h1. {}\n\n".format(classname))) # lay the attributes if "inherits" in gdclass.attrib: inh = gdclass.attrib["inherits"].strip() - clsf.write( - "*Inherits:* \"{name}({title})\":/class_{link}\n". - format( - name=classname, - title="Go to page of class " + classname, - link=classname.lower())) + clsf.write(tb(("h4. Inherits: \"{name}" + "({title})\":/class_{link}\n\n"). + format( + name=inh, + title="Go to page of class " + inh, + link=classname.lower()))) if "category" in gdclass.attrib: - clsf.write("*Category:* {}". - format(gdclass.attrib["category"].strip())) + clsf.write(tb("h4. Category: {}\n\n". + format(gdclass.attrib["category"].strip()))) # lay child nodes - for gdchild in gdclass.iter(): - if gdchild.tag == "brief_description": - clsf.write("h2. Brief Description\n") - clsf.write(toOP(gdchild.text.strip())) - # convert commands in text + briefd = gdclass.find("brief_description") + logging.debug( + "Brief description was found?: {}".format(briefd)) + if briefd.text.strip(): + logging.debug( + "Text: {}".format(briefd.text.strip())) + clsf.write(tb("h2. Brief Description\n\n")) + clsf.write(tb(toOP(briefd.text.strip()) + + "\"read more\":#more\n\n")) + + # Write the list of member functions of this class + methods = gdclass.find("methods") + if methods and len(methods) > 0: + clsf.write(tb("\nh3. Member Functions\n\n")) + for method in methods.iter(tag='method'): + clsf.write(tb(mkfn(method))) + + signals = gdclass.find("signals") + if signals and len(signals) > 0: + clsf.write(tb("\nh3. Signals\n\n")) + for signal in signals.iter(tag='signal'): + clsf.write(tb(mkfn(signal, True))) + # TODO: tag is necessary to process? it does not + # exists in class_list.xml file. + + consts = gdclass.find("constants") + if consts and len(consts) > 0: + clsf.write(tb("\nh3. Numeric Constants\n\n")) + for const in sorted(consts, key=lambda k: + k.attrib["name"]): + if const.text.strip(): + clsf.write(tb("* *{name}* = *{value}* - {desc}\n". + format( + name=const.attrib["name"], + value=const.attrib["value"], + desc=const.text.strip()))) + else: + # Constant have no description + clsf.write(tb("* *{name}* = *{value}*\n". + format( + name=const.attrib["name"], + value=const.attrib["value"]))) + descrip = gdclass.find("description") + clsf.write(tb("\nh3(#more). Description\n\n")) + if descrip.text: + clsf.write(tb(descrip.text.strip() + "\n")) + else: + clsf.write(tb("_Nothing here, yet..._\n")) + + # and finally, the description for each method + if methods and len(methods) > 0: + clsf.write(tb("\nh3. Member Function Description\n\n")) + for method in methods.iter(tag='method'): + clsf.write(tb("h4(#{n}). {name}\n\n".format( + n=method.attrib["name"].lower(), + name=method.attrib["name"]))) + clsf.write(tb(mkfn(method) + "\n")) + clsf.write(tb(toOP(method.find( + "description").text.strip()))) From f36d0fe4c56a7e82a814a682a136dfb07f922861 Mon Sep 17 00:00:00 2001 From: Jack Gold Date: Tue, 29 Sep 2015 22:38:23 -0400 Subject: [PATCH 16/38] Small readability fixes for the in-game class reference - fixes #2515 and #2393 (text spacing and top/left padding for the text area, respectively). --- tools/editor/editor_help.cpp | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/tools/editor/editor_help.cpp b/tools/editor/editor_help.cpp index 213c18e1b0f..46ed2194a83 100644 --- a/tools/editor/editor_help.cpp +++ b/tools/editor/editor_help.cpp @@ -547,6 +547,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->pop(); class_desc->add_newline(); class_desc->add_newline(); + class_desc->add_newline(); } @@ -563,6 +564,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v _add_text(cd.brief_description); class_desc->add_newline(); class_desc->add_newline(); + class_desc->add_newline(); } bool method_descr=false; @@ -637,7 +639,6 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v if (cd.properties.size()) { - class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/keyword_color")); class_desc->push_font(doc_title_font); class_desc->add_text("Members:"); @@ -715,9 +716,10 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->add_newline(); } - class_desc->add_newline(); class_desc->pop(); + class_desc->add_newline(); + class_desc->add_newline(); } if (cd.signals.size()) { @@ -779,6 +781,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->pop(); class_desc->add_newline(); + class_desc->add_newline(); } @@ -823,6 +826,7 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->pop(); class_desc->add_newline(); + class_desc->add_newline(); } @@ -830,17 +834,18 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v if (cd.description!="") { description_line=class_desc->get_line_count()-2; + class_desc->push_color(EditorSettings::get_singleton()->get("text_editor/keyword_color")); class_desc->push_font(doc_title_font); class_desc->add_text("Description:"); class_desc->pop(); class_desc->pop(); - class_desc->add_newline(); class_desc->add_newline(); _add_text(cd.description); class_desc->add_newline(); class_desc->add_newline(); + class_desc->add_newline(); } if (method_descr) { @@ -853,12 +858,16 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->add_newline(); class_desc->add_newline(); + class_desc->push_indent(1); for(int i=0;iget_line_count()-2; + if( cd.methods[i].description != "") { + class_desc->add_newline(); + } class_desc->push_font(doc_code_font); _add_type(cd.methods[i].return_type); @@ -899,9 +908,12 @@ Error EditorHelp::_goto_desc(const String& p_class,bool p_update_history,int p_v class_desc->pop(); - class_desc->add_newline(); - class_desc->add_newline(); - _add_text(cd.methods[i].description); + if( cd.methods[i].description != "") { + class_desc->add_text(" "); + _add_text(cd.methods[i].description); + class_desc->add_newline(); + class_desc->add_newline(); + } class_desc->add_newline(); class_desc->add_newline(); @@ -1392,6 +1404,8 @@ EditorHelp::EditorHelp(EditorNode *p_editor) { PanelContainer *pc = memnew( PanelContainer ); Ref style( memnew( StyleBoxFlat ) ); style->set_bg_color( EditorSettings::get_singleton()->get("text_editor/background_color") ); + style->set_default_margin(MARGIN_LEFT,20); + style->set_default_margin(MARGIN_TOP,20); pc->add_style_override("panel", style); //get_stylebox("normal","TextEdit")); h_split->add_child(pc); class_desc = memnew( RichTextLabel ); From 1630f0ad3518422c3be76f6289f4b1d3e724907d Mon Sep 17 00:00:00 2001 From: volzhs Date: Wed, 30 Sep 2015 21:55:31 +0900 Subject: [PATCH 17/38] fix miss bind for Node::find_node --- scene/main/node.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 8336ce35f60..5c7e420c7b5 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1931,7 +1931,7 @@ void Node::_bind_methods() { ObjectTypeDB::bind_method(_MD("has_node","path"),&Node::has_node); ObjectTypeDB::bind_method(_MD("get_node:Node","path"),&Node::get_node); ObjectTypeDB::bind_method(_MD("get_parent:Parent"),&Node::get_parent); - ObjectTypeDB::bind_method(_MD("find_node:Node","mask","recursive","owned"),&Node::get_node,DEFVAL(true),DEFVAL(true)); + ObjectTypeDB::bind_method(_MD("find_node:Node","mask","recursive","owned"),&Node::find_node,DEFVAL(true),DEFVAL(true)); ObjectTypeDB::bind_method(_MD("has_node_and_resource","path"),&Node::has_node_and_resource); ObjectTypeDB::bind_method(_MD("get_node_and_resource","path"),&Node::_get_node_and_resource); From a609a567ea033b99aaa7ad005313d39bec514172 Mon Sep 17 00:00:00 2001 From: Franklin Sobrinho Date: Sun, 4 Oct 2015 08:29:19 -0300 Subject: [PATCH 18/38] Fix bug in EditorPlugin::add_custom_control, fixes #2567 --- tools/editor/editor_plugin.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/editor/editor_plugin.cpp b/tools/editor/editor_plugin.cpp index 04c34d9a88e..7417d707bb0 100644 --- a/tools/editor/editor_plugin.cpp +++ b/tools/editor/editor_plugin.cpp @@ -74,6 +74,7 @@ void EditorPlugin::add_custom_control(CustomControlContainer p_location,Control case CONTAINER_CANVAS_EDITOR_SIDE: { CanvasItemEditor::get_singleton()->get_palette_split()->add_child(p_control); + CanvasItemEditor::get_singleton()->get_palette_split()->move_child(p_control,0); } break; case CONTAINER_CANVAS_EDITOR_BOTTOM: { From 7f001a2c762d50d7502fefde477487b911ddee47 Mon Sep 17 00:00:00 2001 From: Zher Huei Lee Date: Mon, 5 Oct 2015 11:50:16 +0100 Subject: [PATCH 19/38] nested clipping of canvas items now works --- servers/visual/visual_server_raster.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/servers/visual/visual_server_raster.cpp b/servers/visual/visual_server_raster.cpp index fbea60c3a6b..cf840c1be37 100644 --- a/servers/visual/visual_server_raster.cpp +++ b/servers/visual/visual_server_raster.cpp @@ -6824,7 +6824,11 @@ void VisualServerRaster::_render_canvas_item(CanvasItem *p_canvas_item,const Mat copymem(child_items,ci->child_items.ptr(),child_item_count*sizeof(CanvasItem*)); if (ci->clip) { - ci->final_clip_rect=global_rect; + if (p_canvas_clip != NULL) { + ci->final_clip_rect=p_canvas_clip->final_clip_rect.clip(global_rect); + } else { + ci->final_clip_rect=global_rect; + } ci->final_clip_owner=ci; } else { From 7c843fbc048a869eb4a03f774e9271d7f18c4d68 Mon Sep 17 00:00:00 2001 From: Jorge Araya Navarro Date: Tue, 6 Oct 2015 19:50:55 -0600 Subject: [PATCH 20/38] Refactoring some strings and ordering imports --- tools/docdump/makedocs.py | 122 ++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 70 deletions(-) diff --git a/tools/docdump/makedocs.py b/tools/docdump/makedocs.py index 81469eed9c6..303c94f79b4 100644 --- a/tools/docdump/makedocs.py +++ b/tools/docdump/makedocs.py @@ -3,9 +3,7 @@ # # makedocs.py: Generate documentation for Open Project Wiki -# # Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. -# # Contributor: Jorge Araya Navarro # @@ -24,7 +22,7 @@ # whitespace you may left. # # TODO: -# * Refactor code. Refactor strings. +# * Refactor code. # * Adapt this script for generating content in other markup formats like # DokuWiki, Markdown, etc. # * Because people will translate class_list.xml, we should implement @@ -32,17 +30,33 @@ # # Also check other TODO entries in this script for more information on what is # left to do. - -import re import argparse import logging -from os import path +import re from itertools import zip_longest +from os import path from xml.etree import ElementTree + # add an option to change the verbosity logging.basicConfig(level=logging.INFO) +# Strings +C_LINK = ("\"{gclass}(Go to page of class" + " {gclass})\":/class_{lkclass}") +MC_LINK = ("\"{gclass}.{method}(Go " + "to page {gclass}, section {method})\"" + ":/class_{lkclass}#{lkmethod}") +TM_JUMP = ("\"{method}(Jump to method" + " {method})\":#{lkmethod}") +GTC_LINK = " \"{rtype}(Go to page of class {rtype})\":/class_{link} " +DFN_JUMP = ("\"*{funcname}*(Jump to description for" + " node {funcname})\":#{link} ( ") +M_ARG_DEFAULT = C_LINK + " {name}={default}" +M_ARG = C_LINK + " {name}" + +OPENPROJ_INH = ("h4. Inherits: " + C_LINK + "\n\n") + def tb(string): """ Return a byte representation of a string @@ -69,9 +83,7 @@ def sortkey(c): def toOP(text): """ Convert commands in text to Open Project commands """ - # We are going to do something very complicated with all HTML commands - # sadly, some commands are embedded inside other commands, so some steps - # are needed before converting some commands to Textile markup + # TODO: Make this capture content between [command] ... [/command] groups = re.finditer((r'\[html (?P/?\w+/?)(\]| |=)?(\]| |=)?(?P\w+)?(\]| |=)?(?P"[^"]+")?/?\]'), text) alignstr = "" @@ -106,27 +118,21 @@ def toOP(text): for group in groups: gd = group.groupdict() if gd["class"]: - replacewith = ("\"{gclass}.{method}(Go " - "to page {gclass}, section {method})\"" - ":/class_{lkclass}#{lkmethod}". - format(gclass=gd["class"], - method=gd["method"], - lkclass=gd["class"].lower(), - lkmethod=gd["method"].lower())) + replacewith = (MC_LINK.format(gclass=gd["class"], + method=gd["method"], + lkclass=gd["class"].lower(), + lkmethod=gd["method"].lower())) else: # The method is located in the same wiki page - replacewith = ("\"{method}(Jump to method" - " {method})\":#{lkmethod}". - format(method=gd["method"], - lkmethod=gd["method"].lower())) + replacewith = (TM_JUMP.format(method=gd["method"], + lkmethod=gd["method"].lower())) text = text.replace(group.group(0), replacewith, 1) # Finally, [Classes] are around brackets, make them direct links groups = re.finditer(r'\[(?P[az0-AZ0_]+)\]', text) for group in groups: gd = group.groupdict() - replacewith = ("\"{gclass}(Go to page of class" - " {gclass})\":/class_{lkclass}". + replacewith = (C_LINK. format(gclass=gd["class"], lkclass=gd["class"].lower())) text = text.replace(group.group(0), replacewith, 1) @@ -149,18 +155,16 @@ def mkfn(node, is_signal=False): # return type if not is_signal: if rtype != "void": - finalstr += " \"{rtype}({title})\":/class_{link} ".format( + finalstr += GTC_LINK.format( rtype=rtype, - title="Go to page of class " + rtype, link=rtype.lower()) else: finalstr += " void " # function name if not is_signal: - finalstr += "\"*{funcname}*({title})\":#{link} ( ".format( + finalstr += DFN_JUMP.format( funcname=name, - title="Jump to description for node " + name, link=name.lower()) else: # Signals have no description @@ -171,31 +175,19 @@ def mkfn(node, is_signal=False): node.iter(tag="argument"), key=lambda a: int(a.attrib["index"])): - twd = "{type} {name}={default}" - tnd = "{type} {name}" - tlk = ("\"*{cls}*(Go to page of class {cls})" - "\":/class_{lcls}") ntype = arg.attrib["type"] nname = arg.attrib["name"] if "default" in arg.attrib: - args.insert( - -1, - twd.format( - type=tlk.format( - cls=ntype, - lcls=ntype.lower()), - name=nname, - default=arg.attrib["default"])) + args.insert(-1, M_ARG_DEFAULT.format( + gclass=ntype, + lkclass=ntype.lower(), + name=nname, + default=arg.attrib["default"])) else: # No default value present - args.insert( - -1, - tnd.format( - type=tlk.format( - cls=ntype, - lcls=ntype.lower()), - name=nname)) + args.insert(-1, M_ARG.format(gclass=ntype, + lkclass=ntype.lower(), name=nname)) # join the arguments together finalstr += ", ".join(args) # and, close the function with a ) @@ -265,10 +257,9 @@ with open(path.join(args.outputdir, "class_list.txt"), "wb") as fcl: # empty cell fcl.write(tb("| |")) # write the class name column, left - fcl.write(tb("\"{name}({title})\":/class_{link}".format( - name=gdclassl.attrib["name"], - title="Go to page of class " + gdclassl.attrib["name"], - link="class_" + gdclassl.attrib["name"].lower()))) + fcl.write(tb(C_LINK.format( + gclass=gdclassl.attrib["name"], + lkclass=gdclassl.attrib["name"].lower()))) # write the index symbol column, right if isinstance(gdclassr, ElementTree.Element): @@ -288,10 +279,9 @@ with open(path.join(args.outputdir, "class_list.txt"), "wb") as fcl: continue # write the class name column (if any), right - fcl.write(tb("\"{name}({title})\":/{link} |\n".format( - name=gdclassr.attrib["name"], - title="Go to page of class " + gdclassr.attrib["name"], - link=gdclassr.attrib["name"].lower()))) + fcl.write(tb(C_LINK.format( + gclass=gdclassl.attrib["name"], + lkclass=gdclassl.attrib["name"].lower()) + "|\n")) # row written # # now, let's write each class page for each class @@ -307,36 +297,28 @@ with open(path.join(args.outputdir, "class_list.txt"), "wb") as fcl: # lay the attributes if "inherits" in gdclass.attrib: inh = gdclass.attrib["inherits"].strip() - clsf.write(tb(("h4. Inherits: \"{name}" - "({title})\":/class_{link}\n\n"). - format( - name=inh, - title="Go to page of class " + inh, - link=classname.lower()))) + clsf.write(tb(OPENPROJ_INH.format(gclass=inh, + lkclass=inh.lower()))) if "category" in gdclass.attrib: clsf.write(tb("h4. Category: {}\n\n". format(gdclass.attrib["category"].strip()))) # lay child nodes briefd = gdclass.find("brief_description") - logging.debug( - "Brief description was found?: {}".format(briefd)) if briefd.text.strip(): - logging.debug( - "Text: {}".format(briefd.text.strip())) - clsf.write(tb("h2. Brief Description\n\n")) + clsf.write(b"h2. Brief Description\n\n") clsf.write(tb(toOP(briefd.text.strip()) + "\"read more\":#more\n\n")) # Write the list of member functions of this class methods = gdclass.find("methods") if methods and len(methods) > 0: - clsf.write(tb("\nh3. Member Functions\n\n")) + clsf.write(b"\nh3. Member Functions\n\n") for method in methods.iter(tag='method'): clsf.write(tb(mkfn(method))) signals = gdclass.find("signals") if signals and len(signals) > 0: - clsf.write(tb("\nh3. Signals\n\n")) + clsf.write(b"\nh3. Signals\n\n") for signal in signals.iter(tag='signal'): clsf.write(tb(mkfn(signal, True))) # TODO: tag is necessary to process? it does not @@ -344,7 +326,7 @@ with open(path.join(args.outputdir, "class_list.txt"), "wb") as fcl: consts = gdclass.find("constants") if consts and len(consts) > 0: - clsf.write(tb("\nh3. Numeric Constants\n\n")) + clsf.write(b"\nh3. Numeric Constants\n\n") for const in sorted(consts, key=lambda k: k.attrib["name"]): if const.text.strip(): @@ -360,15 +342,15 @@ with open(path.join(args.outputdir, "class_list.txt"), "wb") as fcl: name=const.attrib["name"], value=const.attrib["value"]))) descrip = gdclass.find("description") - clsf.write(tb("\nh3(#more). Description\n\n")) + clsf.write(b"\nh3(#more). Description\n\n") if descrip.text: clsf.write(tb(descrip.text.strip() + "\n")) else: - clsf.write(tb("_Nothing here, yet..._\n")) + clsf.write(b"_Nothing here, yet..._\n") # and finally, the description for each method if methods and len(methods) > 0: - clsf.write(tb("\nh3. Member Function Description\n\n")) + clsf.write(b"\nh3. Member Function Description\n\n") for method in methods.iter(tag='method'): clsf.write(tb("h4(#{n}). {name}\n\n".format( n=method.attrib["name"].lower(), From 68d005760bcfc498aad61719630b7ae21b4f31d8 Mon Sep 17 00:00:00 2001 From: Jorge Araya Navarro Date: Wed, 7 Oct 2015 13:11:17 -0600 Subject: [PATCH 21/38] Internationalization implemented Specify which language you want the templates translate to with the `--language` option and any choice language available. By default English is used. When class_list.xml is translated to different language than English this option will be useful to make the template language match. --- .../locales/es/LC_MESSAGES/makedocs.mo | Bin 0 -> 2321 bytes .../locales/es/LC_MESSAGES/makedocs.po | 142 ++++++++++++++++++ tools/docdump/makedocs.pot | 108 +++++++++++++ tools/docdump/makedocs.py | 136 ++++++++++------- 4 files changed, 330 insertions(+), 56 deletions(-) create mode 100644 tools/docdump/locales/es/LC_MESSAGES/makedocs.mo create mode 100644 tools/docdump/locales/es/LC_MESSAGES/makedocs.po create mode 100644 tools/docdump/makedocs.pot diff --git a/tools/docdump/locales/es/LC_MESSAGES/makedocs.mo b/tools/docdump/locales/es/LC_MESSAGES/makedocs.mo new file mode 100644 index 0000000000000000000000000000000000000000..8d7ea2689efec8e01fe5b79e69e4bd5266317e59 GIT binary patch literal 2321 zcmbuA&u<$=6vr1REiAuEi^LC6^);0ssj?e84Q;e>Li4k@iqj|wsNxdFyAylK?u?lk zM~RG&oDkwvaRrGBf{0e!xcXRe;tV&g{0B&UXTAOrD?+UpY5dvw_2#{qH(&pG;mmyp z4*m_k0iJ!@ab5;5gEv4QEQ1^j!Jol3@ZvMD z34RP-1it~VfIouP-fubo0lNIw9lP&oJnvDw0_(Lt^alK z74Qpi9=s23fxm-w@SSsxQvtsQ-v|Eytv{EZ!x&fuZM^qDYgd7cm38d4l347w$r-!W+Ji%arM!J@{7y4%*u~Mn zd)kwVgp4>1Cc@0s+(4uA) z&Tv>}%8D2^vqBRK*4;T%8z@CriH4uvX==0062o#Ux#D!AXfVYHhX~n`|5tWK+&m#Y z-Q9-Xd0!kKUwcOEXBW^rh9{Sx-jO7PJ1|-Jxkz<2cc6sp9RDS0=Q}-6q16P!lj)A^ zD-rz3#0h6f{4myrJG+aWUj+#k;qIXA?6YR)cpye6(y6NWfz`JmHR>F*AwBv4Y6d$I o-cKe-ov%a`humg^?i^cLl9>=~NMvEwjf}Cn-qoikneICO0@jC{jQ{`u literal 0 HcmV?d00001 diff --git a/tools/docdump/locales/es/LC_MESSAGES/makedocs.po b/tools/docdump/locales/es/LC_MESSAGES/makedocs.po new file mode 100644 index 00000000000..82115dd8972 --- /dev/null +++ b/tools/docdump/locales/es/LC_MESSAGES/makedocs.po @@ -0,0 +1,142 @@ +# Translations template for PROJECT. +# Copyright (C) 2015 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2015. +# +msgid "" +msgstr "" +"Project-Id-Version: makedocs\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2015-10-07 11:47-0600\n" +"PO-Revision-Date: 2015-10-07 13:10-0600\n" +"Last-Translator: Jorge Araya Navarro \n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.0\n" +"X-Generator: Poedit 1.8.4\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: makedocs.py:74 +msgid "" +"\"{gclass}(Go to page of class {gclass})\":/class_{lkclass}" +msgstr "" +"\"{gclass}(Ir a la pagina de la clase {gclass})\":/" +"class_{lkclass}" + +#: makedocs.py:76 +msgid "" +"\"{gclass}.{method}(Go to page {gclass}, section {method})\":/" +"class_{lkclass}#{lkmethod}" +msgstr "" +"\"{gclass}.{method}(Ir a la pagina {gclass}, sección " +"{method})\":/class_{lkclass}#{lkmethod}" + +#: makedocs.py:79 +msgid "\"{method}(Jump to method {method})\":#{lkmethod}" +msgstr "\"{method}(Saltar al método {method})\":#{lkmethod}" + +#: makedocs.py:81 +msgid " \"{rtype}(Go to page of class {rtype})\":/class_{link} " +msgstr " \"{rtype}(Ir a la pagina de la clase {rtype})\":/class_{link} " + +#: makedocs.py:82 +msgid "" +"\"*{funcname}*(Jump to description for node {funcname})\":#{link} ( " +msgstr "" +"\"*{funcname}*(Saltar a la descripción para el nodo {funcname})\":#{link} " +"( " + +#: makedocs.py:87 +msgid "h4. Inherits: " +msgstr "h4. Hereda de: " + +#: makedocs.py:232 +msgid "'s version attribute missing" +msgstr "El atributo version de no existe" + +#: makedocs.py:246 +msgid "|_. Index symbol |_. Class name |_. Index symbol |_. Class name |\n" +msgstr "" +"|_. Índice de símbolo |_. Nombre de la clase |_. Índice de símbolo |_. " +"Nombre de la clase |\n" + +#: makedocs.py:305 +msgid "" +"h4. Category: {}\n" +"\n" +msgstr "" +"h4. Categoría: {}\n" +"\n" + +#: makedocs.py:310 +msgid "" +"h2. Brief Description\n" +"\n" +msgstr "" +"h2. Descripción breve\n" +"\n" + +#: makedocs.py:312 +msgid "" +"\"read more\":#more\n" +"\n" +msgstr "" +"\"Leer más\":#more\n" +"\n" + +#: makedocs.py:317 +msgid "" +"\n" +"h3. Member Functions\n" +"\n" +msgstr "" +"\n" +"h3. Funciones miembro\n" +"\n" + +#: makedocs.py:323 +msgid "" +"\n" +"h3. Signals\n" +"\n" +msgstr "" +"\n" +"h3. Señales\n" +"\n" + +#: makedocs.py:331 +msgid "" +"\n" +"h3. Numeric Constants\n" +"\n" +msgstr "" +"\n" +"h3. Constantes numéricas\n" +"\n" + +#: makedocs.py:347 +msgid "" +"\n" +"h3(#more). Description\n" +"\n" +msgstr "" +"\n" +"h3(#more). Descripción\n" +"\n" + +#: makedocs.py:351 +msgid "_Nothing here, yet..._\n" +msgstr "_Aún nada por aquí..._\n" + +#: makedocs.py:355 +msgid "" +"\n" +"h3. Member Function Description\n" +"\n" +msgstr "" +"\n" +"h3. Descripción de las funciones miembro\n" +"\n" diff --git a/tools/docdump/makedocs.pot b/tools/docdump/makedocs.pot new file mode 100644 index 00000000000..be3220f686c --- /dev/null +++ b/tools/docdump/makedocs.pot @@ -0,0 +1,108 @@ +# Translations template for PROJECT. +# Copyright (C) 2015 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2015. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: makedocs 0.1\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2015-10-07 11:47-0600\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.0\n" +"X-Generator: Poedit 1.8.4\n" + +#: makedocs.py:74 +msgid "\"{gclass}(Go to page of class {gclass})\":/class_{lkclass}" +msgstr "" + +#: makedocs.py:76 +msgid "\"{gclass}.{method}(Go to page {gclass}, section {method})\":/class_{lkclass}#{lkmethod}" +msgstr "" + +#: makedocs.py:79 +msgid "\"{method}(Jump to method {method})\":#{lkmethod}" +msgstr "" + +#: makedocs.py:81 +msgid " \"{rtype}(Go to page of class {rtype})\":/class_{link} " +msgstr "" + +#: makedocs.py:82 +msgid "\"*{funcname}*(Jump to description for node {funcname})\":#{link} ( " +msgstr "" + +#: makedocs.py:87 +msgid "h4. Inherits: " +msgstr "" + +#: makedocs.py:232 +msgid "'s version attribute missing" +msgstr "" + +#: makedocs.py:246 +msgid "|_. Index symbol |_. Class name |_. Index symbol |_. Class name |\n" +msgstr "" + +#: makedocs.py:305 +msgid "" +"h4. Category: {}\n" +"\n" +msgstr "" + +#: makedocs.py:310 +msgid "" +"h2. Brief Description\n" +"\n" +msgstr "" + +#: makedocs.py:312 +msgid "" +"\"read more\":#more\n" +"\n" +msgstr "" + +#: makedocs.py:317 +msgid "" +"\n" +"h3. Member Functions\n" +"\n" +msgstr "" + +#: makedocs.py:323 +msgid "" +"\n" +"h3. Signals\n" +"\n" +msgstr "" + +#: makedocs.py:331 +msgid "" +"\n" +"h3. Numeric Constants\n" +"\n" +msgstr "" + +#: makedocs.py:347 +msgid "" +"\n" +"h3(#more). Description\n" +"\n" +msgstr "" + +#: makedocs.py:351 +msgid "_Nothing here, yet..._\n" +msgstr "" + +#: makedocs.py:355 +msgid "" +"\n" +"h3. Member Function Description\n" +"\n" +msgstr "" diff --git a/tools/docdump/makedocs.py b/tools/docdump/makedocs.py index 303c94f79b4..280ce03532f 100644 --- a/tools/docdump/makedocs.py +++ b/tools/docdump/makedocs.py @@ -25,44 +25,22 @@ # * Refactor code. # * Adapt this script for generating content in other markup formats like # DokuWiki, Markdown, etc. -# * Because people will translate class_list.xml, we should implement -# internalization. # # Also check other TODO entries in this script for more information on what is # left to do. +import gettext import argparse import logging import re from itertools import zip_longest from os import path +from os import listdir from xml.etree import ElementTree # add an option to change the verbosity logging.basicConfig(level=logging.INFO) -# Strings -C_LINK = ("\"{gclass}(Go to page of class" - " {gclass})\":/class_{lkclass}") -MC_LINK = ("\"{gclass}.{method}(Go " - "to page {gclass}, section {method})\"" - ":/class_{lkclass}#{lkmethod}") -TM_JUMP = ("\"{method}(Jump to method" - " {method})\":#{lkmethod}") -GTC_LINK = " \"{rtype}(Go to page of class {rtype})\":/class_{link} " -DFN_JUMP = ("\"*{funcname}*(Jump to description for" - " node {funcname})\":#{link} ( ") -M_ARG_DEFAULT = C_LINK + " {name}={default}" -M_ARG = C_LINK + " {name}" - -OPENPROJ_INH = ("h4. Inherits: " + C_LINK + "\n\n") - - -def tb(string): - """ Return a byte representation of a string - """ - return bytes(string, "UTF-8") - def getxmlfloc(): """ Returns the supposed location of the XML file @@ -71,6 +49,72 @@ def getxmlfloc(): return path.join(filepath, "class_list.xml") +def langavailable(): + """ Return a list of languages available for translation + """ + filepath = path.join( + path.dirname(path.abspath(__file__)), "locales") + files = listdir(filepath) + choices = [x for x in files] + choices.insert(0, "none") + return choices + + +desc = "Generates documentation from a XML file to different markup languages" + +parser = argparse.ArgumentParser(description=desc) +parser.add_argument("--input", dest="xmlfp", default=getxmlfloc(), + help="Input XML file, default: {}".format(getxmlfloc())) +parser.add_argument("--output-dir", dest="outputdir", required=True, + help="Output directory for generated files") +parser.add_argument("--language", choices=langavailable(), default="none", + help=("Choose the language of translation" + " for the output files. Default is English (none). " + "Note: This is NOT for the documentation itself!")) +# TODO: add an option for outputting different markup formats + +args = parser.parse_args() +# Let's check if the file and output directory exists +if not path.isfile(args.xmlfp): + logging.critical("File not found: {}".format(args.xmlfp)) + exit(1) +elif not path.isdir(args.outputdir): + logging.critical("Path does not exist: {}".format(args.outputdir)) + exit(1) + +_ = gettext.gettext +if args.language != "none": + logging.info("Language changed to: " + args.language) + lang = gettext.translation(domain="makedocs", + localedir="locales", + languages=[args.language]) + lang.install() + + _ = lang.gettext + +# Strings +C_LINK = _("\"{gclass}(Go to page of class" + " {gclass})\":/class_{lkclass}") +MC_LINK = _("\"{gclass}.{method}(Go " + "to page {gclass}, section {method})\"" + ":/class_{lkclass}#{lkmethod}") +TM_JUMP = _("\"{method}(Jump to method" + " {method})\":#{lkmethod}") +GTC_LINK = _(" \"{rtype}(Go to page of class {rtype})\":/class_{link} ") +DFN_JUMP = _("\"*{funcname}*(Jump to description for" + " node {funcname})\":#{link} ( ") +M_ARG_DEFAULT = C_LINK + " {name}={default}" +M_ARG = C_LINK + " {name}" + +OPENPROJ_INH = _("h4. Inherits: ") + C_LINK + "\n\n" + + +def tb(string): + """ Return a byte representation of a string + """ + return bytes(string, "UTF-8") + + def sortkey(c): """ Symbols are first, letters second """ @@ -201,37 +245,17 @@ def mkfn(node, is_signal=False): return finalstr -desc = "Generates documentation from a XML file to different markup languages" - -parser = argparse.ArgumentParser(description=desc) -parser.add_argument("--input", dest="xmlfp", default=getxmlfloc(), - help="Input XML file, default: {}".format(getxmlfloc())) -parser.add_argument("--output-dir", dest="outputdir", required=True, - help="Output directory for generated files") -# TODO: add an option for outputting different markup formats - -args = parser.parse_args() -# Let's check if the file and output directory exists -if not path.isfile(args.xmlfp): - logging.critical("File not found: {}".format(args.xmlfp)) - exit(1) -elif not path.isdir(args.outputdir): - logging.critical("Path does not exist: {}".format(args.outputdir)) - exit(1) - # Let's begin tree = ElementTree.parse(args.xmlfp) root = tree.getroot() # Check version attribute exists in if "version" not in root.attrib: - logging.critical("'s version attribute missing") + logging.critical(_("'s version attribute missing")) exit(1) version = root.attrib["version"] classes = sorted(root, key=sortkey) -logging.debug("Number of classes: {}".format(len(classes))) -logging.debug("len(classes) / 2 + 1: {}".format(int(len(classes) / 2 + 1))) # first column is always longer, second column of classes should be shorter zclasses = zip_longest(classes[:int(len(classes) / 2 + 1)], classes[int(len(classes) / 2 + 1):], @@ -241,8 +265,8 @@ zclasses = zip_longest(classes[:int(len(classes) / 2 + 1)], with open(path.join(args.outputdir, "class_list.txt"), "wb") as fcl: # Write header of table fcl.write(tb("|^.\n")) - fcl.write(tb("|_. Index symbol |_. Class name " - "|_. Index symbol |_. Class name |\n")) + fcl.write(tb(_("|_. Index symbol |_. Class name " + "|_. Index symbol |_. Class name |\n"))) fcl.write(tb("|-.\n")) indexletterl = "" @@ -300,25 +324,25 @@ with open(path.join(args.outputdir, "class_list.txt"), "wb") as fcl: clsf.write(tb(OPENPROJ_INH.format(gclass=inh, lkclass=inh.lower()))) if "category" in gdclass.attrib: - clsf.write(tb("h4. Category: {}\n\n". + clsf.write(tb(_("h4. Category: {}\n\n"). format(gdclass.attrib["category"].strip()))) # lay child nodes briefd = gdclass.find("brief_description") if briefd.text.strip(): - clsf.write(b"h2. Brief Description\n\n") + clsf.write(tb(_("h2. Brief Description\n\n"))) clsf.write(tb(toOP(briefd.text.strip()) + - "\"read more\":#more\n\n")) + _("\"read more\":#more\n\n"))) # Write the list of member functions of this class methods = gdclass.find("methods") if methods and len(methods) > 0: - clsf.write(b"\nh3. Member Functions\n\n") + clsf.write(tb(_("\nh3. Member Functions\n\n"))) for method in methods.iter(tag='method'): clsf.write(tb(mkfn(method))) signals = gdclass.find("signals") if signals and len(signals) > 0: - clsf.write(b"\nh3. Signals\n\n") + clsf.write(tb(_("\nh3. Signals\n\n"))) for signal in signals.iter(tag='signal'): clsf.write(tb(mkfn(signal, True))) # TODO: tag is necessary to process? it does not @@ -326,7 +350,7 @@ with open(path.join(args.outputdir, "class_list.txt"), "wb") as fcl: consts = gdclass.find("constants") if consts and len(consts) > 0: - clsf.write(b"\nh3. Numeric Constants\n\n") + clsf.write(tb(_("\nh3. Numeric Constants\n\n"))) for const in sorted(consts, key=lambda k: k.attrib["name"]): if const.text.strip(): @@ -342,15 +366,15 @@ with open(path.join(args.outputdir, "class_list.txt"), "wb") as fcl: name=const.attrib["name"], value=const.attrib["value"]))) descrip = gdclass.find("description") - clsf.write(b"\nh3(#more). Description\n\n") + clsf.write(tb(_("\nh3(#more). Description\n\n"))) if descrip.text: clsf.write(tb(descrip.text.strip() + "\n")) else: - clsf.write(b"_Nothing here, yet..._\n") + clsf.write(tb(_("_Nothing here, yet..._\n"))) # and finally, the description for each method if methods and len(methods) > 0: - clsf.write(b"\nh3. Member Function Description\n\n") + clsf.write(tb(_("\nh3. Member Function Description\n\n"))) for method in methods.iter(tag='method'): clsf.write(tb("h4(#{n}). {name}\n\n".format( n=method.attrib["name"].lower(), From 0fb91ef95b7887eae1a8a7741f3e20e66c2b4998 Mon Sep 17 00:00:00 2001 From: Jorge Araya Navarro Date: Wed, 7 Oct 2015 13:15:28 -0600 Subject: [PATCH 22/38] Minor changes --- tools/docdump/makedocs.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/docdump/makedocs.py b/tools/docdump/makedocs.py index 280ce03532f..921b049bd89 100644 --- a/tools/docdump/makedocs.py +++ b/tools/docdump/makedocs.py @@ -84,7 +84,6 @@ elif not path.isdir(args.outputdir): _ = gettext.gettext if args.language != "none": - logging.info("Language changed to: " + args.language) lang = gettext.translation(domain="makedocs", localedir="locales", languages=[args.language]) From 613962d48ad3dd131ef8847d5e65df1fbe2da054 Mon Sep 17 00:00:00 2001 From: Jorge Araya Navarro Date: Wed, 7 Oct 2015 13:24:52 -0600 Subject: [PATCH 23/38] Minor changes: Organizing imports --- tools/docdump/makedocs.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/docdump/makedocs.py b/tools/docdump/makedocs.py index 921b049bd89..be57891abce 100644 --- a/tools/docdump/makedocs.py +++ b/tools/docdump/makedocs.py @@ -28,13 +28,12 @@ # # Also check other TODO entries in this script for more information on what is # left to do. -import gettext import argparse +import gettext import logging import re from itertools import zip_longest -from os import path -from os import listdir +from os import path, listdir from xml.etree import ElementTree From bbca86577d5f8fafce1abc471088909f2630735f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BF=91=E8=97=A4=20=E7=9B=B4=E4=BA=BA?= Date: Fri, 9 Oct 2015 21:39:50 +0900 Subject: [PATCH 24/38] fix parentheses-equality warnings of osx clang --- core/io/unzip.c | 2 +- core/io/zip.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/io/unzip.c b/core/io/unzip.c index 0cd975211e2..b438021ad7d 100644 --- a/core/io/unzip.c +++ b/core/io/unzip.c @@ -1788,7 +1788,7 @@ extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) return UNZ_PARAMERROR; - if ((pfile_in_zip_read_info->read_buffer == NULL)) + if (pfile_in_zip_read_info->read_buffer==NULL) return UNZ_END_OF_LIST_OF_FILE; if (len==0) return 0; diff --git a/core/io/zip.c b/core/io/zip.c index 8f6aeb922f2..c4ab93ab816 100644 --- a/core/io/zip.c +++ b/core/io/zip.c @@ -1114,9 +1114,9 @@ extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, zi->ci.flag = flagBase; if ((level==8) || (level==9)) zi->ci.flag |= 2; - if ((level==2)) + if (level==2) zi->ci.flag |= 4; - if ((level==1)) + if (level==1) zi->ci.flag |= 6; if (password != NULL) zi->ci.flag |= 1; From 13ff4bde650149577185d6d4c61b924cf0896d98 Mon Sep 17 00:00:00 2001 From: eska Date: Fri, 9 Oct 2015 21:54:16 +0200 Subject: [PATCH 25/38] Fix Area2D type mask matching --- servers/physics_2d/space_2d_sw.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/servers/physics_2d/space_2d_sw.cpp b/servers/physics_2d/space_2d_sw.cpp index a71e6c4bf5e..9f2f03baeca 100644 --- a/servers/physics_2d/space_2d_sw.cpp +++ b/servers/physics_2d/space_2d_sw.cpp @@ -36,8 +36,8 @@ _FORCE_INLINE_ static bool _match_object_type_query(CollisionObject2DSW *p_objec if ((p_object->get_layer_mask()&p_layer_mask)==0) return false; - if (p_object->get_type()==CollisionObject2DSW::TYPE_AREA && !(p_type_mask&Physics2DDirectSpaceState::TYPE_MASK_AREA)) - return false; + if (p_object->get_type()==CollisionObject2DSW::TYPE_AREA) + return p_type_mask&Physics2DDirectSpaceState::TYPE_MASK_AREA; Body2DSW *body = static_cast(p_object); From 375a7a727f215a1d790de9caf82cb2a5e89cf711 Mon Sep 17 00:00:00 2001 From: eska Date: Sat, 10 Oct 2015 02:33:26 +0200 Subject: [PATCH 26/38] Fix 3D Area mask matching --- servers/physics/space_sw.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/servers/physics/space_sw.cpp b/servers/physics/space_sw.cpp index d36b0049896..ba1c737530f 100644 --- a/servers/physics/space_sw.cpp +++ b/servers/physics/space_sw.cpp @@ -34,10 +34,10 @@ _FORCE_INLINE_ static bool _match_object_type_query(CollisionObjectSW *p_object, uint32_t p_layer_mask, uint32_t p_type_mask) { - if ((p_object->get_layer_mask()&p_layer_mask)==0) - return false; + if (p_object->get_type()==CollisionObjectSW::TYPE_AREA) + return p_type_mask&PhysicsDirectSpaceState::TYPE_MASK_AREA; - if (p_object->get_type()==CollisionObjectSW::TYPE_AREA && !(p_type_mask&PhysicsDirectSpaceState::TYPE_MASK_AREA)) + if ((p_object->get_layer_mask()&p_layer_mask)==0) return false; BodySW *body = static_cast(p_object); From 793c53122ae155842beadf8742239d1c071cc1fd Mon Sep 17 00:00:00 2001 From: eska Date: Sat, 10 Oct 2015 05:46:47 +0200 Subject: [PATCH 27/38] Add type mask property to RayCast2D --- scene/2d/ray_cast_2d.cpp | 17 ++++++++++++++++- scene/2d/ray_cast_2d.h | 4 ++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp index 05594fd79cc..acc4c620e67 100644 --- a/scene/2d/ray_cast_2d.cpp +++ b/scene/2d/ray_cast_2d.cpp @@ -53,6 +53,16 @@ uint32_t RayCast2D::get_layer_mask() const { return layer_mask; } +void RayCast2D::set_type_mask(uint32_t p_mask) { + + type_mask=p_mask; +} + +uint32_t RayCast2D::get_type_mask() const { + + return type_mask; +} + bool RayCast2D::is_colliding() const{ return collided; @@ -162,7 +172,7 @@ void RayCast2D::_notification(int p_what) { Physics2DDirectSpaceState::RayResult rr; - if (dss->intersect_ray(gt.get_origin(),gt.xform(to),rr,exclude,layer_mask)) { + if (dss->intersect_ray(gt.get_origin(),gt.xform(to),rr,exclude,layer_mask,type_mask)) { collided=true; against=rr.collider_id; @@ -241,9 +251,13 @@ void RayCast2D::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_layer_mask","mask"),&RayCast2D::set_layer_mask); ObjectTypeDB::bind_method(_MD("get_layer_mask"),&RayCast2D::get_layer_mask); + ObjectTypeDB::bind_method(_MD("set_type_mask","mask"),&RayCast2D::set_type_mask); + ObjectTypeDB::bind_method(_MD("get_type_mask"),&RayCast2D::get_type_mask); + ADD_PROPERTY(PropertyInfo(Variant::BOOL,"enabled"),_SCS("set_enabled"),_SCS("is_enabled")); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2,"cast_to"),_SCS("set_cast_to"),_SCS("get_cast_to")); ADD_PROPERTY(PropertyInfo(Variant::INT,"layer_mask",PROPERTY_HINT_ALL_FLAGS),_SCS("set_layer_mask"),_SCS("get_layer_mask")); + ADD_PROPERTY(PropertyInfo(Variant::INT,"type_mask",PROPERTY_HINT_FLAGS,"Static,Kinematic,Rigid,Character,Area"),_SCS("set_type_mask"),_SCS("get_type_mask")); } RayCast2D::RayCast2D() { @@ -253,5 +267,6 @@ RayCast2D::RayCast2D() { collided=false; against_shape=0; layer_mask=1; + type_mask=Physics2DDirectSpaceState::TYPE_MASK_COLLISION; cast_to=Vector2(0,50); } diff --git a/scene/2d/ray_cast_2d.h b/scene/2d/ray_cast_2d.h index c7616be5233..8c3ce8b3b38 100644 --- a/scene/2d/ray_cast_2d.h +++ b/scene/2d/ray_cast_2d.h @@ -44,6 +44,7 @@ class RayCast2D : public Node2D { Vector2 collision_normal; Set exclude; uint32_t layer_mask; + uint32_t type_mask; Vector2 cast_to; @@ -62,6 +63,9 @@ public: void set_layer_mask(uint32_t p_mask); uint32_t get_layer_mask() const; + void set_type_mask(uint32_t p_mask); + uint32_t get_type_mask() const; + bool is_colliding() const; Object *get_collider() const; int get_collider_shape() const; From 422929e87fbe91be1efedf1fe3a9a71d61e58b40 Mon Sep 17 00:00:00 2001 From: reduz Date: Sat, 10 Oct 2015 09:09:09 -0300 Subject: [PATCH 28/38] Large improvements on scene packing and management -Ability to edit and keep changes of instanced scenes and sub-scenes -Ability to inherit from other scenes --- core/path_db.cpp | 31 + core/path_db.h | 5 +- core/resource.h | 2 +- demos/2d/platformer/coin.gd | 8 + scene/main/node.cpp | 64 +- scene/main/node.h | 27 +- scene/register_scene_types.cpp | 11 +- scene/resources/packed_scene.cpp | 1121 ++++++++++++++++++++++--- scene/resources/packed_scene.h | 98 ++- scene/resources/scene_format_text.cpp | 792 +++++++++++++++++ scene/resources/scene_format_text.h | 46 + tools/editor/editor_node.cpp | 18 +- tools/editor/editor_node.h | 3 +- tools/editor/property_editor.cpp | 151 +++- tools/editor/property_editor.h | 7 +- tools/editor/reparent_dialog.cpp | 1 + tools/editor/scene_tree_dock.cpp | 51 +- tools/editor/scene_tree_editor.cpp | 93 +- tools/editor/scene_tree_editor.h | 7 +- 19 files changed, 2313 insertions(+), 223 deletions(-) create mode 100644 scene/resources/scene_format_text.cpp create mode 100644 scene/resources/scene_format_text.h diff --git a/core/path_db.cpp b/core/path_db.cpp index d3dc3aceb8d..c6ea25d9666 100644 --- a/core/path_db.cpp +++ b/core/path_db.cpp @@ -286,6 +286,37 @@ NodePath::NodePath(const Vector& p_path,const Vector& p_ data->property=p_property; } + +void NodePath::simplify() { + + if (!data) + return; + for(int i=0;ipath.size();i++) { + if (data->path.size()==1) + break; + if (data->path[i].operator String()==".") { + data->path.remove(i); + i--; + } else if (data->path[i].operator String()==".." && i>0 && data->path[i-1].operator String()!="." && data->path[i-1].operator String()!="..") { + //remove both + data->path.remove(i-1); + data->path.remove(i-1); + i-=2; + if (data->path.size()==0) { + data->path.push_back("."); + break; + } + } + } +} + +NodePath NodePath::simplified() const { + + NodePath np=*this; + np.simplify(); + return np; +} + NodePath::NodePath(const String& p_path) { data=NULL; diff --git a/core/path_db.h b/core/path_db.h index b4f13d50bef..de84216006f 100644 --- a/core/path_db.h +++ b/core/path_db.h @@ -84,7 +84,10 @@ public: bool operator==(const NodePath& p_path) const; bool operator!=(const NodePath& p_path) const; void operator=(const NodePath& p_path); - + + void simplify(); + NodePath simplified() const; + NodePath(const Vector& p_path,bool p_absolute,const String& p_property=""); NodePath(const Vector& p_path,const Vector& p_subpath,bool p_absolute,const String& p_property=""); NodePath(const NodePath& p_path); diff --git a/core/resource.h b/core/resource.h index 9d9c445e1d3..3596abe6731 100644 --- a/core/resource.h +++ b/core/resource.h @@ -130,7 +130,7 @@ public: void set_name(const String& p_name); String get_name() const; - void set_path(const String& p_path,bool p_take_over=false); + virtual void set_path(const String& p_path,bool p_take_over=false); String get_path() const; void set_subindex(int p_sub_index); diff --git a/demos/2d/platformer/coin.gd b/demos/2d/platformer/coin.gd index 3e31a395ae2..983cd46d888 100644 --- a/demos/2d/platformer/coin.gd +++ b/demos/2d/platformer/coin.gd @@ -18,3 +18,11 @@ func _ready(): # Initalization here pass + + +func _on_coin_area_enter( area ): + pass # replace with function body + + +func _on_coin_area_enter_shape( area_id, area, area_shape, area_shape ): + pass # replace with function body diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 8336ce35f60..b02e9c5645d 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -841,6 +841,20 @@ Node *Node::get_child(int p_index) const { return data.children[p_index]; } + +Node *Node::_get_child_by_name(const StringName& p_name) const { + + int cc=data.children.size(); + Node* const* cd=data.children.ptr(); + + for(int i=0;idata.name==p_name) + return cd[i]; + } + + return NULL; +} + Node *Node::_get_node(const NodePath& p_path) const { ERR_FAIL_COND_V( !data.inside_tree && p_path.is_absolute(), NULL ); @@ -906,8 +920,10 @@ Node *Node::_get_node(const NodePath& p_path) const { Node *Node::get_node(const NodePath& p_path) const { Node *node = _get_node(p_path); - ERR_EXPLAIN("Node not found: "+p_path); - ERR_FAIL_COND_V(!node,NULL); + if (!node) { + ERR_EXPLAIN("Node not found: "+p_path); + ERR_FAIL_COND_V(!node,NULL); + } return node; } @@ -1332,7 +1348,29 @@ String Node::get_filename() const { return data.filename; } +void Node::set_editable_instance(Node* p_node,bool p_editable) { + ERR_FAIL_NULL(p_node); + ERR_FAIL_COND(!is_a_parent_of(p_node)); + NodePath p = get_path_to(p_node); + if (!p_editable) + data.editable_instances.erase(p); + else + data.editable_instances[p]=true; + +} + +bool Node::is_editable_instance(Node *p_node) const { + + if (!p_node) + return false; //easier, null is never editable :) + ERR_FAIL_COND_V(!is_a_parent_of(p_node),false); + NodePath p = get_path_to(p_node); + return data.editable_instances.has(p); +} + + +#if 0 void Node::generate_instance_state() { @@ -1383,6 +1421,28 @@ Dictionary Node::get_instance_state() const { return data.instance_state; } +#endif + +void Node::set_scene_instance_state(const Ref& p_state) { + + data.instance_state=p_state; +} + +Ref Node::get_scene_instance_state() const{ + + return data.instance_state; +} + +void Node::set_scene_inherited_state(const Ref& p_state) { + + data.inherited_state=p_state; +} + +Ref Node::get_scene_inherited_state() const{ + + return data.inherited_state; +} + Vector Node::get_instance_groups() const { return data.instance_groups; diff --git a/scene/main/node.h b/scene/main/node.h index a6d5bfbd9fd..55557c6356d 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -38,6 +38,7 @@ class Viewport; +class SceneState; class Node : public Object { OBJ_TYPE( Node, Object ); @@ -69,7 +70,11 @@ private: struct Data { String filename; - Dictionary instance_state; + Ref instance_state; + Ref inherited_state; + + HashMap editable_instances; + Vector instance_groups; Vector instance_connections; @@ -96,6 +101,7 @@ private: PauseMode pause_mode; Node *pause_owner; // variables used to properly sort the node when processing, ignored otherwise + //should move all the stuff below to bits bool fixed_process; bool idle_process; @@ -105,6 +111,7 @@ private: bool parent_owned; bool in_constructor; + } data; @@ -112,6 +119,7 @@ private: virtual bool _use_builtin_script() const { return true; } Node *_get_node(const NodePath& p_path) const; + Node *_get_child_by_name(const StringName& p_name) const; @@ -151,7 +159,7 @@ protected: static void _bind_methods(); -friend class PackedScene; +friend class SceneState; void _add_child_nocheck(Node* p_child,const StringName& p_name); void _set_owner_nocheck(Node* p_owner); @@ -208,7 +216,7 @@ public: struct GroupInfo { - String name; + StringName name; bool persistent; }; @@ -229,7 +237,10 @@ public: void set_filename(const String& p_filename); String get_filename() const; - + + void set_editable_instance(Node* p_node,bool p_editable); + bool is_editable_instance(Node* p_node) const; + /* NOTIFICATIONS */ void propagate_notification(int p_notification); @@ -261,8 +272,12 @@ public: //Node *clone_tree() const; // used by editors, to save what has changed only - void generate_instance_state(); - Dictionary get_instance_state() const; + void set_scene_instance_state(const Ref& p_state); + Ref get_scene_instance_state() const; + + void set_scene_inherited_state(const Ref& p_state); + Ref get_scene_inherited_state() const; + Vector get_instance_groups() const; Vector get_instance_connections() const; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 851de4a89f9..ede586dc8d4 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -216,7 +216,7 @@ #include "scene/3d/collision_polygon.h" #endif - +#include "scene/resources/scene_format_text.h" static ResourceFormatLoaderImage *resource_loader_image=NULL; static ResourceFormatLoaderWAV *resource_loader_wav=NULL; @@ -229,6 +229,8 @@ static ResourceFormatLoaderBitMap *resource_loader_bitmap=NULL; static ResourceFormatLoaderTheme *resource_loader_theme=NULL; static ResourceFormatLoaderShader *resource_loader_shader=NULL; +static ResourceFormatSaverText *resource_saver_text=NULL; + //static SceneStringNames *string_names; void register_scene_types() { @@ -612,6 +614,9 @@ void register_scene_types() { OS::get_singleton()->yield(); //may take time to init + resource_saver_text = memnew( ResourceFormatSaverText ); + ResourceSaver::add_resource_format_saver(resource_saver_text); + } void unregister_scene_types() { @@ -629,5 +634,9 @@ void unregister_scene_types() { memdelete( resource_loader_theme ); memdelete( resource_loader_shader ); + + if (resource_saver_text) { + memdelete(resource_saver_text); + } SceneStringNames::free(); } diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index b6082c3a761..86e7fe7f76d 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -33,12 +33,28 @@ #include "scene/gui/control.h" #include "scene/2d/node_2d.h" -bool PackedScene::can_instance() const { +#define PACK_VERSION 2 + +bool SceneState::can_instance() const { return nodes.size()>0; } -Node *PackedScene::instance(bool p_gen_edit_state) const { + +Node *SceneState::instance(bool p_gen_edit_state) const { + + // nodes where instancing failed (because something is missing) + List stray_instances; + +#define NODE_FROM_ID(p_name,p_id)\ + Node *p_name;\ + if (p_id&FLAG_ID_IS_PATH) {\ + NodePath np=node_paths[p_id&FLAG_MASK];\ + p_name=ret_nodes[0]->_get_node(np);\ + } else {\ + ERR_FAIL_INDEX_V(p_id&FLAG_MASK,nc,NULL);\ + p_name=ret_nodes[p_id&FLAG_MASK];\ + } int nc = nodes.size(); ERR_FAIL_COND_V(nc==0,NULL); @@ -59,29 +75,58 @@ Node *PackedScene::instance(bool p_gen_edit_state) const { Node **ret_nodes=(Node**)alloca( sizeof(Node*)*nc ); + bool gen_node_path_cache=p_gen_edit_state && node_path_cache.empty(); for(int i=0;i0) { + + NODE_FROM_ID(nparent,n.parent); +#ifdef DEBUG_ENABLED + if (!nparent && n.parent&FLAG_ID_IS_PATH) { + + WARN_PRINT(String("Parent path '"+String(node_paths[n.parent&FLAG_MASK])+"' for node '"+String(snames[n.name])+"' has vanished when instancing: '"+get_path()+"'.").ascii().get_data()); + + } +#endif + parent=nparent; } Node *node=NULL; - if (n.instance>=0) { - //instance existing + if (i==0 && base_scene_idx>=0) { + //scene inheritance on root node + Ref sdata = props[ base_scene_idx ]; + ERR_FAIL_COND_V( !sdata.is_valid(), NULL); + node = sdata->instance(p_gen_edit_state); + ERR_FAIL_COND_V(!node,NULL); + if (p_gen_edit_state) { + node->set_scene_inherited_state(sdata->get_state()); + } + + } else if (n.instance>=0) { + //instance a scene into this node Ref sdata = props[ n.instance ]; ERR_FAIL_COND_V( !sdata.is_valid(), NULL); - node = sdata->instance(); + node = sdata->instance(p_gen_edit_state); ERR_FAIL_COND_V(!node,NULL); - if (p_gen_edit_state) - node->generate_instance_state(); - } else { - //create anew + } else if (n.type==TYPE_INSTANCED) { + //get the node from somewhere, it likely already exists from another instance + if (parent) { + node=parent->_get_child_by_name(snames[n.name]); +#ifdef DEBUG_ENABLED + if (!node) { + WARN_PRINT(String("Node '"+String(ret_nodes[0]->get_path_to(parent))+"/"+String(snames[n.name])+"' was modified from inside a instance, but it has vanished.").ascii().get_data()); + } +#endif + } + } else if (ObjectTypeDB::is_type_enabled(snames[n.type])) { + //node belongs to this scene and must be created Object * obj = ObjectTypeDB::instance(snames[ n.type ]); if (!obj || !obj->cast_to()) { if (obj) { @@ -109,49 +154,68 @@ Node *PackedScene::instance(bool p_gen_edit_state) const { } - //properties - int nprop_count=n.properties.size(); - if (nprop_count) { + if (node) { + // may not have found the node (part of instanced scene and removed) + // if found all is good, otherwise ignore - const NodeData::Property* nprops=&n.properties[0]; + //properties + int nprop_count=n.properties.size(); + if (nprop_count) { - for(int j=0;jset(snames[ nprops[j].name ],props[ nprops[j].value ],&valid); + bool valid; + ERR_FAIL_INDEX_V( nprops[j].name, sname_count, NULL ); + ERR_FAIL_INDEX_V( nprops[j].value, prop_count, NULL ); + + node->set(snames[ nprops[j].name ],props[ nprops[j].value ],&valid); + } } - } - //name + //name + + //groups + for(int j=0;jadd_to_group( snames[ n.groups[j] ], true ); + } + + if (n.instance>=0 || n.type!=TYPE_INSTANCED) { + //if node was not part of instance, must set it's name, parenthood and ownership + if (i>0) { + if (parent) { + parent->_add_child_nocheck(node,snames[n.name]); + } else { + //it may be possible that an instanced scene has changed + //and the node has nowhere to go anymore + stray_instances.push_back(node); //can't be added, go to stray list + } + } else { + node->_set_name_nocheck( snames[ n.name ] ); + } + + } + + if (n.owner>=0) { + + NODE_FROM_ID(owner,n.owner); + if (owner) + node->_set_owner_nocheck(owner); + } - //groups - for(int j=0;jadd_to_group( snames[ n.groups[j] ], true ); } ret_nodes[i]=node; - if (i>0) { - ERR_FAIL_INDEX_V(n.parent,i,NULL); - ERR_FAIL_COND_V(!ret_nodes[n.parent],NULL); - ret_nodes[n.parent]->_add_child_nocheck(node,snames[n.name]); - } else { - node->_set_name_nocheck( snames[ n.name ] ); + if (node && gen_node_path_cache && ret_nodes[0]) { + NodePath n = ret_nodes[0]->get_path_to(node); + node_path_cache[n]=i; } - - - if (n.owner>=0) { - - ERR_FAIL_INDEX_V(n.owner,i,NULL); - node->_set_owner_nocheck(ret_nodes[n.owner]); - } - } @@ -163,8 +227,14 @@ Node *PackedScene::instance(bool p_gen_edit_state) const { for(int i=0;i binds; if (c.binds.size()) { @@ -173,17 +243,25 @@ Node *PackedScene::instance(bool p_gen_edit_state) const { binds[j]=props[ c.binds[j] ]; } - if (!ret_nodes[c.from] || !ret_nodes[c.to]) - continue; - ret_nodes[c.from]->connect( snames[ c.signal], ret_nodes[ c.to ], snames[ c.method], binds,CONNECT_PERSIST|c.flags ); + + cfrom->connect( snames[ c.signal], cto, snames[ c.method], binds,CONNECT_PERSIST|c.flags ); } - Node *s = ret_nodes[0]; + //Node *s = ret_nodes[0]; - if (get_path()!="" && get_path().find("::")==-1) - s->set_filename(get_path()); + //remove nodes that could not be added, likely as a result that + while(stray_instances.size()) { + memdelete(stray_instances.front()->get()); + stray_instances.pop_front();; + } + + for(int i=0;i_get_node(editable_instances[i]); + if (ei) { + ret_nodes[0]->set_editable_instance(ei,true); + } + } - s->notification(Node::NOTIFICATION_INSTANCED); return ret_nodes[0]; } @@ -209,44 +287,150 @@ static int _vm_get_variant(const Variant& p_variant, HashMap &name_map,HashMap &variant_map,Map &node_map) { +Error SceneState::_parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map &name_map,HashMap &variant_map,Map &node_map,Map &nodepath_map) { - if (p_node!=p_owner && (p_node->get_owner()!=p_owner)) - return OK; //nothing to do with this node, may either belong to another scene or be onowned + + // this function handles all the work related to properly packing scenes, be it + // instanced or inherited. + // given the complexity of this process, an attempt will be made to properly + // document it. if you fail to understand something, please ask! + + //discard nodes that do not belong to be processed + if (p_node!=p_owner && p_node->get_owner()!=p_owner && !p_owner->is_editable_instance(p_node->get_owner())) + return OK; + + // save the child instanced scenes that are chosen as editable, so they can be restored + // upon load back + if (p_node!=p_owner && p_node->get_filename()!=String() && p_owner->is_editable_instance(p_node)) + editable_instances.push_back(p_owner->get_path_to(p_node)); NodeData nd; nd.name=_nm_get_string(p_node->get_name(),name_map); - nd.type=_nm_get_string(p_node->get_type(),name_map); - nd.parent=p_parent_idx; + nd.instance=-1; //not instanced by default + // if this node is part of an instanced scene or sub-instanced scene + // we need to get the corresponding instance states. + // with the instance states, we can query for identical properties/groups + // and only save what has changed - Dictionary instance_state; - Set instance_groups; + List pack_state_stack; + bool instanced_by_owner=true; - if (p_node!=p_owner && p_node->get_filename()!="") { - //instanced + { + Node *n=p_node; + + while(n) { + + if (n==p_owner) { + + Ref state = n->get_scene_inherited_state(); + if (state.is_valid()) { + int node = state->find_node_by_path(n->get_path_to(p_node)); + if (node>=0) { + //this one has state for this node, save + PackState ps; + ps.node=node; + ps.state=state; + pack_state_stack.push_front(ps); + instanced_by_owner=false; + } + } + + if (p_node->get_filename()!=String() && p_node->get_owner()==p_owner && instanced_by_owner) { + //must instance ourselves + Ref instance = ResourceLoader::load(p_node->get_filename()); + if (!instance.is_valid()) { + return ERR_CANT_OPEN; + } + + nd.instance=_vm_get_variant(instance,variant_map); + } + n=NULL; + } else { + if (n->get_filename()!=String()) { + //is an instance + Ref state = n->get_scene_instance_state(); + if (state.is_valid()) { + int node = state->find_node_by_path(n->get_path_to(p_node)); + if (node>=0) { + //this one has state for this node, save + PackState ps; + ps.node=node; + ps.state=state; + pack_state_stack.push_back(ps); + } + } + + } + n=n->get_owner(); + } + } + } + +#if 0 + + Ref base_scene = p_node->get_scene_inherited_state(); //for inheritance + Ref instance_state; + int instance_state_node=-1; + + if (base_scene.is_valid() && (p_node==p_owner || p_node->get_owner()==p_owner)) { + //scene inheritance in use, see if this node is actually inherited + NodePath path = p_owner->get_path_to(p_node); + instance_state_node = base_scene->find_node_by_path(path); + if (instance_state_node>=0) { + instance_state=base_scene; + } + } + + // check that this is a directly instanced scene from the scene being packed, if so + // this information must be saved. Of course, if using scene instancing and this node + // does belong to base scene, ignore. + + if (instance_state.is_null() && p_node!=p_owner && p_node->get_owner()==p_owner && p_node->get_filename()!="") { + + //instanced, only direct sub-scnes are supported of course Ref instance = ResourceLoader::load(p_node->get_filename()); if (!instance.is_valid()) { return ERR_CANT_OPEN; } nd.instance=_vm_get_variant(instance,variant_map); - instance_state = p_node->get_instance_state(); - Vector ig = p_node->get_instance_groups(); - for(int i=0;iget_owner()==p_owner && p_node->get_filename()!=String()) { + instance_state=p_node->get_scene_instance_state(); + if (instance_state.is_valid()) { + instance_state_node=instance_state->find_node_by_path(p_node->get_path_to(p_node)); + } + + } else if (p_node->get_owner()!=p_owner && p_owner->is_editable_instance(p_node->get_owner())) { + instance_state=p_node->get_owner()->get_scene_instance_state(); + if (instance_state.is_valid()) { + instance_state_node=instance_state->find_node_by_path(p_node->get_owner()->get_path_to(p_node)); + } + } + } +#endif + int subscene_prop_search_from=0; + + // all setup, we then proceed to check all properties for the node + // and save the ones that are worth saving List plist; p_node->get_property_list(&plist); + + for (List::Element *E=plist.front();E;E=E->next()) { @@ -257,34 +441,63 @@ Error PackedScene::_parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map< String name = E->get().name; Variant value = p_node->get( E->get().name ); - if (nd.instance<0 && ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONZERO) && value.is_zero()) || ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONONE) && value.is_one())) { - continue; - } - print_line("PASSED!"); - print_line("at: "+String(p_node->get_name())+"::"+name+": - nz: "+itos(E->get().usage&PROPERTY_USAGE_STORE_IF_NONZERO)+" no: "+itos(E->get().usage&PROPERTY_USAGE_STORE_IF_NONONE)); - print_line("value: "+String(value)+" is zero: "+itos(value.is_zero())+" is one" +itos(value.is_one())); + bool isdefault = ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONZERO) && value.is_zero()) || ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONONE) && value.is_one()); - if (nd.instance>=0) { - //only save changed properties in instance - /* - // this was commented because it would not save properties created from within script - // done with _get_property_list, that are not in the original node. - // if some property is not saved, check again +// if (nd.instance<0 && ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONZERO) && value.is_zero()) || ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONONE) && value.is_one())) { +// continue; +// } - if (!instance_state.has(name)) { - print_line("skip not in instance"); - continue; - }*/ - if (E->get().usage & PROPERTY_USAGE_NO_INSTANCE_STATE) { + + //print_line("PASSED!"); + //print_line("at: "+String(p_node->get_name())+"::"+name+": - nz: "+itos(E->get().usage&PROPERTY_USAGE_STORE_IF_NONZERO)+" no: "+itos(E->get().usage&PROPERTY_USAGE_STORE_IF_NONONE)); + //print_line("value: "+String(value)+" is zero: "+itos(value.is_zero())+" is one" +itos(value.is_one())); + + if (pack_state_stack.size()) { + // we are on part of an instanced subscene + // or part of instanced scene. + // only save what has been changed + // only save changed properties in instance + + if (E->get().usage & PROPERTY_USAGE_NO_INSTANCE_STATE || E->get().name=="__meta__") { + //property has requested that no instance state is saved, sorry + //also, meta won't be overriden or saved continue; } - if (instance_state.has(name) && instance_state[name]==value) { + bool exists=false; + Variant original; + + for (List::Element *F=pack_state_stack.back();F;F=F->prev()) { + //check all levels of pack to see if the property exists somewhere + const PackState &ps=F->get(); + + original = ps.state->get_property_value(ps.node,E->get().name,exists); + if (exists) { + break; + } + } + + + if (exists && bool(Variant::evaluate(Variant::OP_EQUAL,value,original))) { + //exists and did not change continue; } + if (!exists && isdefault) { + //does not exist in original node, but it's the default value + //so safe to skip too. + continue; + } + + + } else { + + if (isdefault) { + //it's the default value, no point in saving it + continue; + } } NodeData::Property prop; @@ -295,6 +508,9 @@ Error PackedScene::_parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map< } + // save the groups this node is into + // discard groups that come from the original scene + List groups; p_node->get_groups(&groups); for(List::Element *E=groups.front();E;E=E->next()) { @@ -302,27 +518,123 @@ Error PackedScene::_parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map< if (!gi.persistent) continue; - if (nd.instance>=0 && instance_groups.has(gi.name)) - continue; //group was instanced, don't add here +// if (instance_state_node>=0 && instance_state->is_node_in_group(instance_state_node,gi.name)) +// continue; //group was instanced, don't add here + + bool skip=false; + for (List::Element *F=pack_state_stack.front();F;F=F->next()) { + //check all levels of pack to see if the group was added somewhere + const PackState &ps=F->get(); + if (ps.state->is_node_in_group(ps.node,gi.name)) { + skip=true; + break; + } + } + + if (skip) + continue; nd.groups.push_back(_nm_get_string(gi.name,name_map)); } - if (node_map.has(p_node->get_owner())) - nd.owner=node_map[p_node->get_owner()]; - else + + // save the right owner + // for the saved scene root this is -1 + // for nodes of the saved scene this is 0 + // for nodes of instanced scenes this is >0 + + if (p_node==p_owner) { + //saved scene root nd.owner=-1; + } else if (p_node->get_owner()==p_owner) { + //part of saved scene + nd.owner=0; + } else { + + nd.owner=-1; +#if 0 + // this is pointless, if this was instanced by something else, + // the owner will already be set. + + if (node_map.has(p_node->get_owner())) { + //maybe an existing saved node + nd.owner=node_map[p_node->get_owner()]; + } else { + //not saved, use nodepath map + int sidx; + if (nodepath_map.has(p_node->get_owner())) { + sidx=nodepath_map[p_node->get_owner()]; + } else { + sidx=nodepath_map.size(); + nodepath_map[p_node->get_owner()]=sidx; + } + + nd.owner=FLAG_ID_IS_PATH|sidx; + + } +#endif + + + } + + // Save the right type. If this node was created by an instance + // then flag that the node should not be created but reused + if (pack_state_stack.empty()) { + //this node is not part of an instancing process, so save the type + nd.type=_nm_get_string(p_node->get_type(),name_map); + } else { + // this node is part of an instanced process, so do not save the type. + // instead, save that it was instanced + nd.type=TYPE_INSTANCED; + } + + + // determine whether to save this node or not + // if this node is part of an instanced sub-scene, we can skip storing it if basically + // no properties changed and no groups were added to it. + // below condition is true for all nodes of the scene being saved, and ones in subscenes + // that hold changes + + bool save_node = nd.properties.size() || nd.groups.size(); // some local properties or groups exist + save_node = save_node || p_node==p_owner; // owner is always saved + save_node = save_node || (p_node->get_owner()==p_owner && instanced_by_owner); //part of scene and not instanced + int idx = nodes.size(); - node_map[p_node]=idx; - nodes.push_back(nd); + int parent_node=NO_PARENT_SAVED; + if (save_node) { + + //don't save the node if nothing and subscene + + node_map[p_node]=idx; + + //ok validate parent node + if (p_parent_idx==NO_PARENT_SAVED) { + + int sidx; + if (nodepath_map.has(p_node->get_parent())) { + sidx=nodepath_map[p_node->get_parent()]; + } else { + sidx=nodepath_map.size(); + nodepath_map[p_node->get_parent()]=sidx; + } + + nd.parent=FLAG_ID_IS_PATH|sidx; + } else { + nd.parent=p_parent_idx; + } + + parent_node=idx; + nodes.push_back(nd); + + } for(int i=0;iget_child_count();i++) { Node *c=p_node->get_child(i); - Error err = _parse_node(p_owner,c,idx,name_map,variant_map,node_map); + Error err = _parse_node(p_owner,c,parent_node,name_map,variant_map,node_map,nodepath_map); if (err) return err; } @@ -331,53 +643,135 @@ Error PackedScene::_parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map< } -Error PackedScene::_parse_connections(Node *p_owner,Node *p_node, Map &name_map,HashMap &variant_map,Map &node_map) { +Error SceneState::_parse_connections(Node *p_owner,Node *p_node, Map &name_map,HashMap &variant_map,Map &node_map,Map &nodepath_map) { - if (p_node!=p_owner && (p_node->get_owner()!=p_owner)) - return OK; //nothing to do with this node, may either belong to another scene or be onowned - - List signals; - p_node->get_signal_list(&signals); - - ERR_FAIL_COND_V( !node_map.has(p_node), ERR_BUG); - NodeData &nd = nodes[node_map[p_node]]; - Set instance_connections; - - if (nd.instance>=0) { - - Vector iconns = p_node->get_instance_connections(); - for(int i=0;iget_owner() && p_node->get_owner()!=p_owner && !p_owner->is_editable_instance(p_node->get_owner())) + return OK; - for(List::Element *E=signals.front();E;E=E->next()) { + List _signals; + p_node->get_signal_list(&_signals); + + //ERR_FAIL_COND_V( !node_map.has(p_node), ERR_BUG); + //NodeData &nd = nodes[node_map[p_node]]; + + + for(List::Element *E=_signals.front();E;E=E->next()) { List conns; p_node->get_signal_connection_list(E->get().name,&conns); for(List::Element *F=conns.front();F;F=F->next()) { const Node::Connection &c = F->get(); - if (!(c.flags&CONNECT_PERSIST)) + + if (!(c.flags&CONNECT_PERSIST)) //only persistent connections get saved continue; - if (nd.instance>=0 && instance_connections.has(c)) - continue; //came from instance, don't save! + // only connections that originate or end into main saved scene are saved + // everything else is discarded Node *n=c.target->cast_to(); - if (!n) - continue; - - if (!node_map.has(n)) { - WARN_PRINT("Connection to node outside scene??") + if (!n) { continue; } + //source node is outside saved scene? + bool src_is_out = p_node!=p_owner && (p_node->get_filename()!=String() || p_node->get_owner()!=p_owner); + //target node is outside saved scene? + bool dst_is_out = n!=p_owner && (n->get_filename()!=String() || n->get_owner()!=p_owner); + + //if both are out, ignore connection + if (src_is_out && dst_is_out) { + continue; + } + + + { + Node *nl=p_node; + + bool exists=false; + + while(nl) { + + if (nl==p_owner) { + + Ref state = nl->get_scene_inherited_state(); + if (state.is_valid()) { + int from_node = state->find_node_by_path(nl->get_path_to(p_node)); + int to_node = state->find_node_by_path(nl->get_path_to(n)); + + if (from_node>=0 && to_node>=0) { + //this one has state for this node, save + if (state->is_connection(from_node,c.signal,to_node,c.method)) { + exists=true; + break; + } + } + } + + nl=NULL; + } else { + if (nl->get_filename()!=String()) { + //is an instance + Ref state = nl->get_scene_instance_state(); + if (state.is_valid()) { + int from_node = state->find_node_by_path(nl->get_path_to(p_node)); + int to_node = state->find_node_by_path(nl->get_path_to(n)); + + if (from_node>=0 && to_node>=0) { + //this one has state for this node, save + if (state->is_connection(from_node,c.signal,to_node,c.method)) { + exists=true; + break; + } + } + } + + } + nl=nl->get_owner(); + } + } + + if (exists) { + continue; + } + + } + + + int src_id; + + if (node_map.has(p_node)) { + src_id=node_map[p_node]; + } else { + if (nodepath_map.has(p_node)) { + src_id=FLAG_ID_IS_PATH|nodepath_map[p_node]; + } else { + int sidx=nodepath_map.size(); + nodepath_map[p_node]=sidx; + src_id=FLAG_ID_IS_PATH|sidx; + } + } + + + + int target_id; + + if (node_map.has(n)) { + target_id=node_map[n]; + } else { + if (nodepath_map.has(n)) { + target_id=FLAG_ID_IS_PATH|nodepath_map[n]; + } else { + int sidx=nodepath_map.size(); + nodepath_map[n]=sidx; + target_id=FLAG_ID_IS_PATH|sidx; + } + } + ConnectionData cd; - cd.from=node_map[p_node]; - cd.to=node_map[n]; + cd.from=src_id; + cd.to=target_id; cd.method=_nm_get_string(c.method,name_map); cd.signal=_nm_get_string(c.signal,name_map); cd.flags=c.flags; @@ -392,7 +786,7 @@ Error PackedScene::_parse_connections(Node *p_owner,Node *p_node, Mapget_child_count();i++) { Node *c=p_node->get_child(i); - Error err = _parse_connections(p_owner,c,name_map,variant_map,node_map); + Error err = _parse_connections(p_owner,c,name_map,variant_map,node_map,nodepath_map); if (err) return err; } @@ -401,7 +795,7 @@ Error PackedScene::_parse_connections(Node *p_owner,Node *p_node, Map name_map; HashMap variant_map; Map node_map; + Map nodepath_map; - Error err = _parse_node(scene,scene,-1,name_map,variant_map,node_map); + //if using scene inheritance, pack the scene it inherits from + if (scene->get_scene_inherited_state().is_valid()) { + String path = scene->get_scene_inherited_state()->get_path(); + Ref instance = ResourceLoader::load(path); + if (instance.is_valid()) { + + base_scene_idx=_vm_get_variant(instance,variant_map); + } + } + //instanced, only direct sub-scnes are supported of course + + + Error err = _parse_node(scene,scene,-1,name_map,variant_map,node_map,nodepath_map); if (err) { clear(); ERR_FAIL_V(err); } - err = _parse_connections(scene,scene,name_map,variant_map,node_map); + err = _parse_connections(scene,scene,name_map,variant_map,node_map,nodepath_map); if (err) { clear(); ERR_FAIL_V(err); @@ -440,19 +847,170 @@ Error PackedScene::pack(Node *p_scene) { variants[idx]=*K; } + node_paths.resize(nodepath_map.size()); + for(Map::Element *E=nodepath_map.front();E;E=E->next()) { + + node_paths[E->get()]=scene->get_path_to(E->key()); + } + + return OK; } -void PackedScene::clear() { +void SceneState::set_path(const String &p_path) { + + path=p_path; +} + +String SceneState::get_path() const{ + + return path; +} + +void SceneState::clear() { names.clear(); variants.clear(); nodes.clear(); connections.clear(); + node_path_cache.clear(); + node_paths.clear(); + editable_instances.clear(); + base_scene_idx=-1; } -void PackedScene::_set_bundled_scene(const Dictionary& d) { +Ref SceneState::_get_base_scene_state() const { + + if (base_scene_idx>=0) { + + Ref ps = variants[base_scene_idx]; + if (ps.is_valid()) { + return ps->get_state(); + } + } + + return Ref(); +} + +int SceneState::find_node_by_path(const NodePath& p_node) const { + + if (!node_path_cache.has(p_node)) { + if (_get_base_scene_state().is_valid()) { + int idx = _get_base_scene_state()->find_node_by_path(p_node); + if (idx>=0) { + if (!base_scene_node_remap.has(idx)) { + int ridx = nodes.size() + base_scene_node_remap.size(); + base_scene_node_remap[ridx]=idx; + } + + return base_scene_node_remap[idx]; + } + } + return -1; + } + + int nid = node_path_cache[p_node]; + + if (_get_base_scene_state().is_valid() && !base_scene_node_remap.has(nid)) { + //for nodes that _do_ exist in current scene, still try to look for + //the node in the instanced scene, as a property may be missing + //from the local one + int idx = _get_base_scene_state()->find_node_by_path(p_node); + base_scene_node_remap[nid]=idx; + + } + + return nid; +} +Variant SceneState::get_property_value(int p_node, const StringName& p_property, bool &found) const { + + found=false; + + ERR_FAIL_COND_V(p_node<0,Variant()); + + if (p_nodeget_property_value(base_scene_node_remap[p_node],p_property,found); + } + + return Variant(); +} + +bool SceneState::is_node_in_group(int p_node,const StringName& p_group) const { + + ERR_FAIL_COND_V(p_node<0,false); + + if (p_nodeis_node_in_group(base_scene_node_remap[p_node],p_group); + } + + return false; +} + +bool SceneState::is_connection(int p_node,const StringName& p_signal,int p_to_node,const StringName& p_to_method) const { + + ERR_FAIL_COND_V(p_node<0,false); + ERR_FAIL_COND_V(p_to_node<0,false); + + if (p_node=0 && method_idx>=0) { + //signal and method strings are stored.. + + for(int i=0;iis_connection(base_scene_node_remap[p_node],p_signal,base_scene_node_remap[p_to_node],p_to_method); + } + + return false; + +} + + +void SceneState::set_bundled_scene(const Dictionary& d) { ERR_FAIL_COND( !d.has("names")); @@ -463,6 +1021,15 @@ void PackedScene::_set_bundled_scene(const Dictionary& d) { ERR_FAIL_COND( !d.has("conns")); // ERR_FAIL_COND( !d.has("path")); + int version=1; + if (d.has("version")) + version=d["version"]; + + if (version>PACK_VERSION) { + ERR_EXPLAIN("Save format version too new!"); + ERR_FAIL(); + } + DVector snames = d["names"]; if (snames.size()) { @@ -540,11 +1107,34 @@ void PackedScene::_set_bundled_scene(const Dictionary& d) { } + Array np; + if (d.has("node_paths")) { + np=d["node_paths"]; + } + node_paths.resize(np.size()); + for(int i=0;i rnames; rnames.resize(names.size()); @@ -605,7 +1195,25 @@ Dictionary PackedScene::_get_bundled_scene() const { } d["conns"]=rconns; - d["version"]=1; + + Array rnode_paths; + rnode_paths.resize(node_paths.size()); + for(int i=0;i=0) { + d["base_scene"]=base_scene_idx; + } + + d["version"]=PACK_VERSION; // d["path"]=path; @@ -614,10 +1222,264 @@ Dictionary PackedScene::_get_bundled_scene() const { } +int SceneState::get_node_count() const { + + return nodes.size(); +} + +StringName SceneState::get_node_type(int p_idx) const { + + ERR_FAIL_INDEX_V(p_idx,nodes.size(),StringName()); + if (nodes[p_idx].type==TYPE_INSTANCED) + return StringName(); + return names[nodes[p_idx].type]; +} + +StringName SceneState::get_node_name(int p_idx) const { + + ERR_FAIL_INDEX_V(p_idx,nodes.size(),StringName()); + return names[nodes[p_idx].name]; +} + +Ref SceneState::get_node_instance(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx,nodes.size(),Ref()); + if (nodes[p_idx].instance>=0) { + return variants[nodes[p_idx].instance]; + } else if (nodes[p_idx].parent<=0 || nodes[p_idx].parent==NO_PARENT_SAVED) { + + if (base_scene_idx>=0) { + return variants[base_scene_idx]; + } + } + + return Ref(); + + +} +Vector SceneState::get_node_groups(int p_idx) const{ + ERR_FAIL_INDEX_V(p_idx,nodes.size(),Vector()); + Vector groups; + for(int i=0;i sub_path; + NodePath base_path; + int nidx=p_idx; + while(true) { + if (nodes[nidx].parent==NO_PARENT_SAVED || nodes[nidx].parent<0) { + + sub_path.insert(0,"."); + break; + } + + if (!p_for_parent || p_idx!=nidx) { + sub_path.insert(0,names[nodes[nidx].name]); + } + + if (nodes[nidx].parent&FLAG_ID_IS_PATH) { + base_path=node_paths[nodes[nidx].parent&FLAG_MASK]; + break; + } else { + nidx=nodes[nidx].parent&FLAG_MASK; + } + } + + for(int i=0;i SceneState::get_editable_instances() const { + return editable_instances; +} + + +SceneState::SceneState() { + + base_scene_idx=-1; +} + + +//////////////// + + + +void PackedScene::_set_bundled_scene(const Dictionary& d) { + + state->set_bundled_scene(d); +} + +Dictionary PackedScene::_get_bundled_scene() const { + + return state->get_bundled_scene(); +} + + +Error PackedScene::pack(Node *p_scene) { + + return state->pack(p_scene); +} + +void PackedScene::clear() { + + state->clear(); +} + +bool PackedScene::can_instance() const { + + return state->can_instance(); +} + +Node *PackedScene::instance(bool p_gen_edit_state) const { + +#ifndef TOOLS_ENABLED + if (p_gen_edit_state) { + ERR_EXPLAIN("Edit state is only for editors, does not work without tools compiled"); + ERR_FAIL_COND_V(p_gen_edit_state,NULL); + } +#endif + + Node *s = state->instance(p_gen_edit_state); + if (!s) + return NULL; + + if (p_gen_edit_state) { + s->set_scene_instance_state(state); + } + + if (get_path()!="" && get_path().find("::")==-1) + s->set_filename(get_path()); + + + s->notification(Node::NOTIFICATION_INSTANCED); + + return s; +} + +Ref PackedScene::get_state() { + + return state; +} + +void PackedScene::set_path(const String& p_path,bool p_take_over) { + + state->set_path(p_path); + Resource::set_path(p_path,p_take_over); +} + void PackedScene::_bind_methods() { ObjectTypeDB::bind_method(_MD("pack","path:Node"),&PackedScene::pack); - ObjectTypeDB::bind_method(_MD("instance:Node"),&PackedScene::instance,DEFVAL(false)); + ObjectTypeDB::bind_method(_MD("instance:Node","gen_edit_state"),&PackedScene::instance,DEFVAL(false)); ObjectTypeDB::bind_method(_MD("can_instance"),&PackedScene::can_instance); ObjectTypeDB::bind_method(_MD("_set_bundled_scene"),&PackedScene::_set_bundled_scene); ObjectTypeDB::bind_method(_MD("_get_bundled_scene"),&PackedScene::_get_bundled_scene); @@ -628,5 +1490,6 @@ void PackedScene::_bind_methods() { PackedScene::PackedScene() { + state = Ref( memnew( SceneState )); } diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index 6c7fa545d4b..2a2271810e9 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -32,18 +32,32 @@ #include "resource.h" #include "scene/main/node.h" -class PackedScene : public Resource { - OBJ_TYPE( PackedScene, Resource ); - RES_BASE_EXTENSION("scn"); +class SceneState : public Reference { + + OBJ_TYPE( SceneState, Reference ); Vector names; Vector variants; + Vector node_paths; + Vector editable_instances; + mutable HashMap node_path_cache; + mutable Map base_scene_node_remap; + + int base_scene_idx; //missing - instances //missing groups //missing - owner //missing - override names and values + enum { + FLAG_ID_IS_PATH=(1<<30), + FLAG_MASK=(1<<24)-1, + NO_PARENT_SAVED=0x7FFFFFFF, + TYPE_INSTANCED=0x7FFFFFFF, + + }; + struct NodeData { int parent; @@ -59,9 +73,15 @@ class PackedScene : public Resource { }; Vector properties; - Vector groups; + Vector groups; + }; + struct PackState { + Ref state; + int node; + PackState() { node=-1; } + }; Vector nodes; @@ -77,16 +97,75 @@ class PackedScene : public Resource { Vector connections; - Error _parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map &name_map,HashMap &variant_map,Map &node_map); - Error _parse_connections(Node *p_owner,Node *p_node, Map &name_map,HashMap &variant_map,Map &node_map); + Error _parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map &name_map,HashMap &variant_map,Map &node_map,Map &nodepath_map); + Error _parse_connections(Node *p_owner,Node *p_node, Map &name_map,HashMap &variant_map,Map &node_map,Map &nodepath_map); + + String path; + + _FORCE_INLINE_ Ref _get_base_scene_state() const; + +public: + + int find_node_by_path(const NodePath& p_node) const; + Variant get_property_value(int p_node,const StringName& p_property,bool &found) const; + bool is_node_in_group(int p_node,const StringName& p_group) const; + bool is_connection(int p_node,const StringName& p_signal,int p_to_node,const StringName& p_to_method) const; + + + void set_bundled_scene(const Dictionary& p_dictionary); + Dictionary get_bundled_scene() const; + + Error pack(Node *p_scene); + + void set_path(const String &p_path); + String get_path() const; + + void clear(); + + bool can_instance() const; + Node *instance(bool p_gen_edit_state=false) const; + + + //build-unbuild API + + int get_node_count() const; + StringName get_node_type(int p_idx) const; + StringName get_node_name(int p_idx) const; + NodePath get_node_path(int p_idx,bool p_for_parent=false) const; + NodePath get_node_owner_path(int p_idx) const; + Ref get_node_instance(int p_idx) const; + Vector get_node_groups(int p_idx) const; + + int get_node_property_count(int p_idx) const; + StringName get_node_property_name(int p_idx,int p_prop) const; + Variant get_node_property_value(int p_idx,int p_prop) const; + + int get_connection_count() const; + NodePath get_connection_source(int p_idx) const; + StringName get_connection_signal(int p_idx) const; + NodePath get_connection_target(int p_idx) const; + StringName get_connection_method(int p_idx) const; + int get_connection_flags(int p_idx) const; + Array get_connection_binds(int p_idx) const; + + Vector get_editable_instances() const; + + SceneState(); +}; + +class PackedScene : public Resource { + + OBJ_TYPE(PackedScene, Resource ); + RES_BASE_EXTENSION("scn"); + + Ref state; void _set_bundled_scene(const Dictionary& p_scene); Dictionary _get_bundled_scene() const; protected: - static void _bind_methods(); public: @@ -98,7 +177,12 @@ public: bool can_instance() const; Node *instance(bool p_gen_edit_state=false) const; + virtual void set_path(const String& p_path,bool p_take_over=false); + + Ref get_state(); + PackedScene(); + }; #endif // SCENE_PRELOADER_H diff --git a/scene/resources/scene_format_text.cpp b/scene/resources/scene_format_text.cpp new file mode 100644 index 00000000000..8403c06ad1c --- /dev/null +++ b/scene/resources/scene_format_text.cpp @@ -0,0 +1,792 @@ +#include "scene_format_text.h" + +#include "globals.h" +#include "version.h" +#include "os/dir_access.h" + +#define FORMAT_VERSION 1 + +void ResourceFormatSaverTextInstance::write_property(const String& p_name,const Variant& p_property,bool *r_ok) { + + if (r_ok) + *r_ok=false; + + if (p_name!=String()) { + f->store_string(p_name+" = "); + } + + switch( p_property.get_type() ) { + + case Variant::NIL: { + f->store_string("null"); + } break; + case Variant::BOOL: { + + f->store_string(p_property.operator bool() ? "true":"false" ); + } break; + case Variant::INT: { + + f->store_string( itos(p_property.operator int()) ); + } break; + case Variant::REAL: { + + f->store_string( rtoss(p_property.operator real_t()) ); + } break; + case Variant::STRING: { + + String str=p_property; + + str="\""+str.c_escape()+"\""; + f->store_string( str ); + } break; + case Variant::VECTOR2: { + + Vector2 v = p_property; + f->store_string("Vector2( "+rtoss(v.x) +", "+rtoss(v.y)+" )" ); + } break; + case Variant::RECT2: { + + Rect2 aabb = p_property; + f->store_string("Rect2( "+rtoss(aabb.pos.x) +", "+rtoss(aabb.pos.y) +", "+rtoss(aabb.size.x) +", "+rtoss(aabb.size.y)+" )" ); + + } break; + case Variant::VECTOR3: { + + Vector3 v = p_property; + f->store_string("Vector3( "+rtoss(v.x) +", "+rtoss(v.y)+", "+rtoss(v.z)+" )"); + } break; + case Variant::PLANE: { + + Plane p = p_property; + f->store_string("Plane( "+rtoss(p.normal.x) +", "+rtoss(p.normal.y)+", "+rtoss(p.normal.z)+", "+rtoss(p.d)+" )" ); + + } break; + case Variant::_AABB: { + + AABB aabb = p_property; + f->store_string("AABB( "+rtoss(aabb.pos.x) +", "+rtoss(aabb.pos.y) +", "+rtoss(aabb.pos.z) +", "+rtoss(aabb.size.x) +", "+rtoss(aabb.size.y) +", "+rtoss(aabb.size.z)+" )" ); + + } break; + case Variant::QUAT: { + + Quat quat = p_property; + f->store_string("Quat( "+rtoss(quat.x)+", "+rtoss(quat.y)+", "+rtoss(quat.z)+", "+rtoss(quat.w)+" )"); + + } break; + case Variant::MATRIX32: { + + String s="Matrix32( "; + Matrix32 m3 = p_property; + for (int i=0;i<3;i++) { + for (int j=0;j<2;j++) { + + if (i!=0 || j!=0) + s+=", "; + s+=rtoss( m3.elements[i][j] ); + } + } + + f->store_string(s+" )"); + + } break; + case Variant::MATRIX3: { + + String s="Matrix3( "; + Matrix3 m3 = p_property; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + + if (i!=0 || j!=0) + s+=", "; + s+=rtoss( m3.elements[i][j] ); + } + } + + f->store_string(s+" )"); + + } break; + case Variant::TRANSFORM: { + + String s="Transform( "; + Transform t = p_property; + Matrix3 &m3 = t.basis; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + + if (i!=0 || j!=0) + s+=", "; + s+=rtoss( m3.elements[i][j] ); + } + } + + s=s+", "+rtoss(t.origin.x) +", "+rtoss(t.origin.y)+", "+rtoss(t.origin.z); + + f->store_string(s+" )"); + } break; + + // misc types + case Variant::COLOR: { + + Color c = p_property; + f->store_string("Color( "+rtoss(c.r) +", "+rtoss(c.g)+", "+rtoss(c.b)+", "+rtoss(c.a)+" )"); + + } break; + case Variant::IMAGE: { + + + Image img=p_property; + + if (img.empty()) { + f->store_string("RawImage()"); + break; + } + + String imgstr="RawImage( "; + imgstr+=itos(img.get_width()); + imgstr+=", "+itos(img.get_height()); + imgstr+=", "+itos(img.get_mipmaps()); + imgstr+=", "; + + switch(img.get_format()) { + + case Image::FORMAT_GRAYSCALE: imgstr+="GRAYSCALE"; break; + case Image::FORMAT_INTENSITY: imgstr+="INTENSITY"; break; + case Image::FORMAT_GRAYSCALE_ALPHA: imgstr+="GRAYSCALE_ALPHA"; break; + case Image::FORMAT_RGB: imgstr+="RGB"; break; + case Image::FORMAT_RGBA: imgstr+="RGBA"; break; + case Image::FORMAT_INDEXED : imgstr+="INDEXED"; break; + case Image::FORMAT_INDEXED_ALPHA: imgstr+="INDEXED_ALPHA"; break; + case Image::FORMAT_BC1: imgstr+="BC1"; break; + case Image::FORMAT_BC2: imgstr+="BC2"; break; + case Image::FORMAT_BC3: imgstr+="BC3"; break; + case Image::FORMAT_BC4: imgstr+="BC4"; break; + case Image::FORMAT_BC5: imgstr+="BC5"; break; + case Image::FORMAT_PVRTC2: imgstr+="PVRTC2"; break; + case Image::FORMAT_PVRTC2_ALPHA: imgstr+="PVRTC2_ALPHA"; break; + case Image::FORMAT_PVRTC4: imgstr+="PVRTC4"; break; + case Image::FORMAT_PVRTC4_ALPHA: imgstr+="PVRTC4_ALPHA"; break; + case Image::FORMAT_ETC: imgstr+="ETC"; break; + case Image::FORMAT_ATC: imgstr+="ATC"; break; + case Image::FORMAT_ATC_ALPHA_EXPLICIT: imgstr+="ATC_ALPHA_EXPLICIT"; break; + case Image::FORMAT_ATC_ALPHA_INTERPOLATED: imgstr+="ATC_ALPHA_INTERPOLATED"; break; + case Image::FORMAT_CUSTOM: imgstr+="CUSTOM"; break; + default: {} + } + + + String s; + + DVector data = img.get_data(); + int len = data.size(); + DVector::Read r = data.read(); + const uint8_t *ptr=r.ptr();; + for (int i=0;i>4], hex[byte&0xF], 0}; + s+=str; + } + + imgstr+=", "; + f->store_string(imgstr); + f->store_string(s); + f->store_string(" )"); + } break; + case Variant::NODE_PATH: { + + String str=p_property; + + str="NodePath(\""+str.c_escape()+"\")"; + f->store_string(str); + + } break; + + case Variant::OBJECT: { + + RES res = p_property; + if (res.is_null()) { + f->store_string("null"); + if (r_ok) + *r_ok=true; + + break; // don't save it + } + + if (external_resources.has(res)) { + + f->store_string("ExtResource( "+itos(external_resources[res]+1)+" )"); + } else { + + if (internal_resources.has(res)) { + f->store_string("SubResource( "+itos(internal_resources[res])+" )"); + } else if (res->get_path().length() && res->get_path().find("::")==-1) { + + //external resource + String path=relative_paths?local_path.path_to_file(res->get_path()):res->get_path(); + f->store_string("Resource( \""+path+"\" )"); + } else { + f->store_string("null"); + ERR_EXPLAIN("Resource was not pre cached for the resource section, bug?"); + ERR_BREAK(true); + //internal resource + } + } + + } break; + case Variant::INPUT_EVENT: { + + f->store_string("InputEvent()"); //will be added later + } break; + case Variant::DICTIONARY: { + + Dictionary dict = p_property; + + List keys; + dict.get_key_list(&keys); + keys.sort(); + + f->store_string("{ "); + for(List::Element *E=keys.front();E;E=E->next()) { + + //if (!_check_type(dict[E->get()])) + // continue; + bool ok; + write_property("",E->get(),&ok); + ERR_CONTINUE(!ok); + + f->store_string(":"); + write_property("",dict[E->get()],&ok); + if (!ok) + write_property("",Variant()); //at least make the file consistent.. + if (E->next()) + f->store_string(", "); + } + + + f->store_string(" }"); + + + } break; + case Variant::ARRAY: { + + f->store_string("[ "); + Array array = p_property; + int len=array.size(); + for (int i=0;i0) + f->store_string(", "); + write_property("",array[i]); + + + } + f->store_string(" ]"); + + } break; + + case Variant::RAW_ARRAY: { + + f->store_string("RawArray( "); + String s; + DVector data = p_property; + int len = data.size(); + DVector::Read r = data.read(); + const uint8_t *ptr=r.ptr();; + for (int i=0;i0) + f->store_string(", "); + uint8_t byte = ptr[i]; + const char hex[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + char str[3]={ hex[byte>>4], hex[byte&0xF], 0}; + f->store_string(str); + + } + + f->store_string(" )"); + + } break; + case Variant::INT_ARRAY: { + + f->store_string("IntArray( "); + DVector data = p_property; + int len = data.size(); + DVector::Read r = data.read(); + const int *ptr=r.ptr();; + + for (int i=0;i0) + f->store_string(", "); + + f->store_string(itos(ptr[i])); + } + + + f->store_string(" )"); + + } break; + case Variant::REAL_ARRAY: { + + f->store_string("FloatArray( "); + DVector data = p_property; + int len = data.size(); + DVector::Read r = data.read(); + const real_t *ptr=r.ptr();; + + for (int i=0;i0) + f->store_string(", "); + f->store_string(rtoss(ptr[i])); + } + + f->store_string(" )"); + + } break; + case Variant::STRING_ARRAY: { + + f->store_string("StringArray( "); + DVector data = p_property; + int len = data.size(); + DVector::Read r = data.read(); + const String *ptr=r.ptr();; + String s; + //write_string("\n"); + + + + for (int i=0;i0) + f->store_string(", "); + String str=ptr[i]; + f->store_string(""+str.c_escape()+"\""); + } + + f->store_string(" )"); + + } break; + case Variant::VECTOR2_ARRAY: { + + f->store_string("Vector2Array( "); + DVector data = p_property; + int len = data.size(); + DVector::Read r = data.read(); + const Vector2 *ptr=r.ptr();; + + for (int i=0;i0) + f->store_string(", "); + f->store_string(rtoss(ptr[i].x)+", "+rtoss(ptr[i].y) ); + } + + f->store_string(" )"); + + } break; + case Variant::VECTOR3_ARRAY: { + + f->store_string("Vector3Array( "); + DVector data = p_property; + int len = data.size(); + DVector::Read r = data.read(); + const Vector3 *ptr=r.ptr();; + + for (int i=0;i0) + f->store_string(", "); + f->store_string(rtoss(ptr[i].x)+", "+rtoss(ptr[i].y)+", "+rtoss(ptr[i].z) ); + } + + f->store_string(" )"); + + } break; + case Variant::COLOR_ARRAY: { + + f->store_string("ColorArray( "); + + DVector data = p_property; + int len = data.size(); + DVector::Read r = data.read(); + const Color *ptr=r.ptr();; + + for (int i=0;i0) + f->store_string(", "); + + f->store_string(rtoss(ptr[i].r)+", "+rtoss(ptr[i].g)+", "+rtoss(ptr[i].b)+", "+rtoss(ptr[i].a) ); + + } + f->store_string(" )"); + + } break; + default: {} + + } + + if (r_ok) + *r_ok=true; + +} + + +void ResourceFormatSaverTextInstance::_find_resources(const Variant& p_variant,bool p_main) { + + + switch(p_variant.get_type()) { + case Variant::OBJECT: { + + + RES res = p_variant.operator RefPtr(); + + if (res.is_null() || external_resources.has(res)) + return; + + if (!p_main && (!bundle_resources ) && res->get_path().length() && res->get_path().find("::") == -1 ) { + int index = external_resources.size(); + external_resources[res]=index; + return; + } + + if (resource_set.has(res)) + return; + + List property_list; + + res->get_property_list( &property_list ); + property_list.sort(); + + List::Element *I=property_list.front(); + + while(I) { + + PropertyInfo pi=I->get(); + + if (pi.usage&PROPERTY_USAGE_STORAGE || (bundle_resources && pi.usage&PROPERTY_USAGE_BUNDLE)) { + + Variant v=res->get(I->get().name); + _find_resources(v); + } + + I=I->next(); + } + + resource_set.insert( res ); //saved after, so the childs it needs are available when loaded + saved_resources.push_back(res); + + } break; + case Variant::ARRAY: { + + Array varray=p_variant; + int len=varray.size(); + for(int i=0;i keys; + d.get_key_list(&keys); + for(List::Element *E=keys.front();E;E=E->next()) { + + Variant v = d[E->get()]; + _find_resources(v); + } + } break; + default: {} + } + +} + + + +Error ResourceFormatSaverTextInstance::save(const String &p_path,const RES& p_resource,uint32_t p_flags) { + + if (p_path.ends_with(".tscn")) { + packed_scene=p_resource; + } + + Error err; + f = FileAccess::open(p_path, FileAccess::WRITE,&err); + ERR_FAIL_COND_V( err, ERR_CANT_OPEN ); + FileAccessRef _fref(f); + + local_path = Globals::get_singleton()->localize_path(p_path); + + relative_paths=p_flags&ResourceSaver::FLAG_RELATIVE_PATHS; + skip_editor=p_flags&ResourceSaver::FLAG_OMIT_EDITOR_PROPERTIES; + bundle_resources=p_flags&ResourceSaver::FLAG_BUNDLE_RESOURCES; + takeover_paths=p_flags&ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS; + if (!p_path.begins_with("res://")) { + takeover_paths=false; + } + + // save resources + _find_resources(p_resource,true); + + if (packed_scene.is_valid()) { + //add instances to external resources if saving a packed scene + for(int i=0;iget_state()->get_node_count();i++) { + Ref instance=packed_scene->get_state()->get_node_instance(i); + if (instance.is_valid() && !external_resources.has(instance)) { + int index = external_resources.size(); + external_resources[instance]=index; + } + } + } + + + ERR_FAIL_COND_V(err!=OK,err); + + { + String title=packed_scene.is_valid()?"[gd_scene ":"[gd_resource "; + if (packed_scene.is_null()) + title+="type=\""+p_resource->get_type()+"\" "; + int load_steps=saved_resources.size()+external_resources.size(); + //if (packed_scene.is_valid()) { + // load_steps+=packed_scene->get_node_count(); + //} + //no, better to not use load steps from nodes, no point to that + + if (load_steps>1) { + title+="load_steps="+itos(load_steps)+" "; + } + title+="format="+itos(FORMAT_VERSION)+""; + //title+="engine_version=\""+itos(VERSION_MAJOR)+"."+itos(VERSION_MINOR)+"\""; + + f->store_string(title); + f->store_line("]\n"); //one empty line + } + + + for(Map::Element *E=external_resources.front();E;E=E->next()) { + + String p = E->key()->get_path(); + + f->store_string("[ext_resource path=\""+p+"\" type=\""+E->key()->get_save_type()+"\" id="+itos(E->get()+1)+"]\n"); //bundled + } + + if (external_resources.size()) + f->store_line(String()); //separate + + Set used_indices; + + for(List::Element *E=saved_resources.front();E;E=E->next()) { + + RES res = E->get(); + if (E->next() && (res->get_path()=="" || res->get_path().find("::") != -1 )) { + + if (res->get_subindex()!=0) { + if (used_indices.has(res->get_subindex())) { + res->set_subindex(0); //repeated + } else { + used_indices.insert(res->get_subindex()); + } + } + } + } + + for(List::Element *E=saved_resources.front();E;E=E->next()) { + + RES res = E->get(); + ERR_CONTINUE(!resource_set.has(res)); + bool main = (E->next()==NULL); + + if (main && packed_scene.is_valid()) + break; //save as a scene + + if (main) { + f->store_line("[resource]\n"); + } else { + String line="[sub_resource "; + if (res->get_subindex()==0) { + int new_subindex=1; + if (used_indices.size()) { + new_subindex=used_indices.back()->get()+1; + } + + res->set_subindex(new_subindex); + used_indices.insert(new_subindex); + } + + int idx = res->get_subindex(); + line+="type=\""+res->get_type()+"\" id="+itos(idx); + f->store_line(line+"]\n"); + if (takeover_paths) { + res->set_path(p_path+"::"+itos(idx),true); + } + + internal_resources[res]=idx; + + } + + + List property_list; + res->get_property_list(&property_list); +// property_list.sort(); + for(List::Element *PE = property_list.front();PE;PE=PE->next()) { + + + if (skip_editor && PE->get().name.begins_with("__editor")) + continue; + + if (PE->get().usage&PROPERTY_USAGE_STORAGE || (bundle_resources && PE->get().usage&PROPERTY_USAGE_BUNDLE)) { + + String name = PE->get().name; + Variant value = res->get(name); + + + if ((PE->get().usage&PROPERTY_USAGE_STORE_IF_NONZERO && value.is_zero())||(PE->get().usage&PROPERTY_USAGE_STORE_IF_NONONE && value.is_one()) ) + continue; + + if (PE->get().type==Variant::OBJECT && value.is_zero()) + continue; + + write_property(name,value); + f->store_string("\n"); + } + + + } + + f->store_string("\n"); + + } + + if (packed_scene.is_valid()) { + //if this is a scene, save nodes and connections! + Ref state = packed_scene->get_state(); + for(int i=0;iget_node_count();i++) { + + StringName type = state->get_node_type(i); + StringName name = state->get_node_name(i); + NodePath path = state->get_node_path(i,true); + NodePath owner = state->get_node_owner_path(i); + Ref instance = state->get_node_instance(i); + Vector groups = state->get_node_groups(i); + + String header="[node"; + header+=" name=\""+String(name)+"\""; + if (type!=StringName()) { + header+=" type=\""+String(type)+"\""; + } + if (path!=NodePath()) { + header+=" parent=\""+String(path.simplified())+"\""; + } + if (owner!=NodePath() && owner!=NodePath(".")) { + header+=" owner=\""+String(owner.simplified())+"\""; + } + + if (groups.size()) { + String sgroups=" groups=[ "; + for(int j=0;j0) + sgroups+=", "; + sgroups+="\""+groups[i].operator String().c_escape()+"\""; + } + sgroups+=" ]"; + header+=sgroups; + } + + f->store_string(header); + + if (instance.is_valid()) { + f->store_string(" instance="); + write_property("",instance); + } + + f->store_line("]\n"); + + for(int j=0;jget_node_property_count(i);j++) { + + write_property(state->get_node_property_name(i,j),state->get_node_property_value(i,j)); + f->store_line(String()); + + } + + if (state->get_node_property_count(i)) { + //add space + f->store_line(String()); + } + + } + + for(int i=0;iget_connection_count();i++) { + + String connstr="[connection"; + connstr+=" signal=\""+String(state->get_connection_signal(i))+"\""; + connstr+=" from=\""+String(state->get_connection_source(i).simplified())+"\""; + connstr+=" to=\""+String(state->get_connection_target(i).simplified())+"\""; + connstr+=" method=\""+String(state->get_connection_method(i))+"\""; + int flags = state->get_connection_flags(i); + if (flags!=Object::CONNECT_PERSIST) { + connstr+=" flags="+itos(flags); + } + + Array binds=state->get_connection_binds(i); + f->store_string(connstr); + if (binds.size()) { + f->store_string(" binds="); + write_property("",binds); + } + + f->store_line("]\n"); + } + + f->store_line(String()); + + Vector editable_instances = state->get_editable_instances(); + for(int i=0;istore_line("[editable path=\""+editable_instances[i].operator String()+"\"]"); + } + } + + if (f->get_error()!=OK && f->get_error()!=ERR_FILE_EOF) { + f->close(); + return ERR_CANT_CREATE; + } + + f->close(); + //memdelete(f); + + return OK; +} + + + +Error ResourceFormatSaverText::save(const String &p_path,const RES& p_resource,uint32_t p_flags) { + + if (p_path.ends_with(".sct") && p_resource->get_type()!="PackedScene") { + return ERR_FILE_UNRECOGNIZED; + } + + ResourceFormatSaverTextInstance saver; + return saver.save(p_path,p_resource,p_flags); + +} + +bool ResourceFormatSaverText::recognize(const RES& p_resource) const { + + + return true; // all recognized! +} +void ResourceFormatSaverText::get_recognized_extensions(const RES& p_resource,List *p_extensions) const { + + p_extensions->push_back("tres"); //text resource + if (p_resource->get_type()=="PackedScene") + p_extensions->push_back("tscn"); //text scene + +} + +ResourceFormatSaverText* ResourceFormatSaverText::singleton=NULL; +ResourceFormatSaverText::ResourceFormatSaverText() { + singleton=this; +} diff --git a/scene/resources/scene_format_text.h b/scene/resources/scene_format_text.h new file mode 100644 index 00000000000..576a78d1832 --- /dev/null +++ b/scene/resources/scene_format_text.h @@ -0,0 +1,46 @@ +#ifndef SCENE_FORMAT_TEXT_H +#define SCENE_FORMAT_TEXT_H + +#include "io/resource_loader.h" +#include "io/resource_saver.h" +#include "os/file_access.h" +#include "scene/resources/packed_scene.h" + +class ResourceFormatSaverTextInstance { + + String local_path; + + Ref packed_scene; + + bool takeover_paths; + bool relative_paths; + bool bundle_resources; + bool skip_editor; + FileAccess *f; + Set resource_set; + List saved_resources; + Map external_resources; + Map internal_resources; + + void _find_resources(const Variant& p_variant,bool p_main=false); + void write_property(const String& p_name,const Variant& p_property,bool *r_ok=NULL); + +public: + + Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0); + + +}; + +class ResourceFormatSaverText : public ResourceFormatSaver { +public: + static ResourceFormatSaverText* singleton; + virtual Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0); + virtual bool recognize(const RES& p_resource) const; + virtual void get_recognized_extensions(const RES& p_resource,List *p_extensions) const; + + ResourceFormatSaverText(); +}; + + +#endif // SCENE_FORMAT_TEXT_H diff --git a/tools/editor/editor_node.cpp b/tools/editor/editor_node.cpp index fb3c7d5d182..1b709df7d80 100644 --- a/tools/editor/editor_node.cpp +++ b/tools/editor/editor_node.cpp @@ -1107,6 +1107,11 @@ void EditorNode::_dialog_action(String p_file) { push_item(res.operator->() ); } break; + case FILE_NEW_INHERITED_SCENE: { + + + load_scene(p_file,false,true); + } break; case FILE_OPEN_SCENE: { @@ -1930,6 +1935,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { } break; + case FILE_NEW_INHERITED_SCENE: case FILE_OPEN_SCENE: { @@ -1950,7 +1956,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { if (scene) { file->set_current_path(scene->get_filename()); }; - file->set_title("Open Scene"); + file->set_title(p_option==FILE_OPEN_SCENE?"Open Scene":"Open Base Scene"); file->popup_centered_ratio(); } break; @@ -3311,7 +3317,7 @@ void EditorNode::fix_dependencies(const String& p_for_file) { dependency_fixer->edit(p_for_file); } -Error EditorNode::load_scene(const String& p_scene, bool p_ignore_broken_deps) { +Error EditorNode::load_scene(const String& p_scene, bool p_ignore_broken_deps,bool p_set_inherited) { if (!is_inside_tree()) { defer_load_scene = p_scene; @@ -3441,6 +3447,13 @@ Error EditorNode::load_scene(const String& p_scene, bool p_ignore_broken_deps) { } */ + if (p_set_inherited) { + Ref state = sdata->get_state(); + state->set_path(lpath); + new_scene->set_scene_inherited_state(state); + } + + set_edited_scene(new_scene); _get_scene_metadata(); /* @@ -4793,6 +4806,7 @@ EditorNode::EditorNode() { file_menu->set_tooltip("Operations with scene files."); p=file_menu->get_popup(); p->add_item("New Scene",FILE_NEW_SCENE); + p->add_item("New Inherited Scene..",FILE_NEW_INHERITED_SCENE); p->add_item("Open Scene..",FILE_OPEN_SCENE,KEY_MASK_CMD+KEY_O); p->add_separator(); p->add_item("Save Scene",FILE_SAVE_SCENE,KEY_MASK_CMD+KEY_S); diff --git a/tools/editor/editor_node.h b/tools/editor/editor_node.h index 53e16d9c581..d52e08191d6 100644 --- a/tools/editor/editor_node.h +++ b/tools/editor/editor_node.h @@ -107,6 +107,7 @@ class EditorNode : public Node { enum MenuOptions { FILE_NEW_SCENE, + FILE_NEW_INHERITED_SCENE, FILE_OPEN_SCENE, FILE_SAVE_SCENE, FILE_SAVE_AS_SCENE, @@ -565,7 +566,7 @@ public: void fix_dependencies(const String& p_for_file); void clear_scene() { _cleanup_scene(); } - Error load_scene(const String& p_scene,bool p_ignore_broken_deps=false); + Error load_scene(const String& p_scene, bool p_ignore_broken_deps=false, bool p_set_inherited=false); Error load_resource(const String& p_scene); bool is_scene_open(const String& p_path); diff --git a/tools/editor/property_editor.cpp b/tools/editor/property_editor.cpp index b513e32c13d..1a07f30e4e6 100644 --- a/tools/editor/property_editor.cpp +++ b/tools/editor/property_editor.cpp @@ -42,6 +42,8 @@ #include "multi_node_edit.h" #include "array_property_edit.h" #include "editor_help.h" +#include "scene/resources/packed_scene.h" + void CustomPropertyEditor::_notification(int p_what) { @@ -1655,25 +1657,101 @@ CustomPropertyEditor::CustomPropertyEditor() { menu->connect("item_pressed",this,"_menu_option"); } +bool PropertyEditor::_might_be_in_instance() { - -Node *PropertyEditor::get_instanced_node() { - - //this sucks badly if (!obj) return NULL; Node *node = obj->cast_to(); + + Node* edited_scene =EditorNode::get_singleton()->get_edited_scene(); + + bool might_be=false; + + while(node) { + + if (node->get_scene_instance_state().is_valid()) { + might_be=true; + break; + } + if (node==edited_scene) { + if (node->get_scene_inherited_state().is_valid()) { + might_be=true; + break; + } + might_be=false; + break; + } + node=node->get_owner(); + } + + return might_be; + +} + +bool PropertyEditor::_get_instanced_node_original_property(const StringName& p_prop,Variant& value) { + + Node *node = obj->cast_to(); + if (!node) - return NULL; + return false; - if (node->get_filename()=="") - return NULL; + Node *orig=node; - if (!node->get_owner()) - return NULL; //scene root i guess + Node* edited_scene =EditorNode::get_singleton()->get_edited_scene(); - return node; + bool found=false; + +// print_line("for prop - "+String(p_prop)); + + while(node) { + + Ref ss; + + if (node==edited_scene) { + ss=node->get_scene_inherited_state(); + } else { + ss=node->get_scene_instance_state(); + } +// print_line("at - "+String(edited_scene->get_path_to(node))); + + if (ss.is_valid()) { + NodePath np = node->get_path_to(orig); + int node_idx = ss->find_node_by_path(np); +// print_line("\t valid, nodeidx "+itos(node_idx)); + if (node_idx>=0) { + bool lfound=false; + Variant lvar; + lvar=ss->get_property_value(node_idx,p_prop,lfound); + if (lfound) { + found=true; + value=lvar; +// print_line("\t found value "+String(value)); + } + } + } + if (node==edited_scene) { + //just in case + break; + } + node=node->get_owner(); + + } + + return found; +} + +bool PropertyEditor::_is_property_different(const Variant& p_current, const Variant& p_orig,int p_usage) { + + if (p_orig.get_type()==Variant::NIL) { + //special cases + if (p_current.is_zero() && p_usage&PROPERTY_USAGE_STORE_IF_NONZERO) + return false; + if (p_current.is_one() && p_usage&PROPERTY_USAGE_STORE_IF_NONONE) + return false; + } + + return bool(Variant::evaluate(Variant::OP_NOT_EQUAL,p_current,p_orig)); } TreeItem *PropertyEditor::find_item(TreeItem *p_item,const String& p_name) { @@ -1910,6 +1988,8 @@ void PropertyEditor::set_item_text(TreeItem *p_item, int p_type, const String& p } + + void PropertyEditor::_notification(int p_what) { if (p_what==NOTIFICATION_ENTER_TREE) { @@ -1950,12 +2030,16 @@ void PropertyEditor::_notification(int p_what) { if (!item) continue; - if (get_instanced_node()) { + if (_might_be_in_instance()) { - Dictionary d = get_instanced_node()->get_instance_state(); - if (d.has(*k)) { + + Variant vorig; + Dictionary d=item->get_metadata(0); + int usage = d.has("usage")?int(int(d["usage"])&(PROPERTY_USAGE_STORE_IF_NONONE|PROPERTY_USAGE_STORE_IF_NONZERO)):0; + + + if (_get_instanced_node_original_property(*k,vorig) || usage) { Variant v = obj->get(*k); - Variant vorig = d[*k]; int found=-1; for(int i=0;iget_button_count(1);i++) { @@ -1966,7 +2050,7 @@ void PropertyEditor::_notification(int p_what) { } } - bool changed = ! (v==vorig); + bool changed = _is_property_different(v,vorig,usage); if ((found!=-1)!=changed) { @@ -2049,12 +2133,14 @@ void PropertyEditor::_refresh_item(TreeItem *p_item) { if (name!=String()) { - if (get_instanced_node()) { + if (_might_be_in_instance()) { - Dictionary d = get_instanced_node()->get_instance_state(); - if (d.has(name)) { + Variant vorig; + Dictionary d=p_item->get_metadata(0); + int usage = d.has("usage")?int(int(d["usage"])&(PROPERTY_USAGE_STORE_IF_NONONE|PROPERTY_USAGE_STORE_IF_NONZERO)):0; + + if (_get_instanced_node_original_property(name,vorig) || usage) { Variant v = obj->get(name); - Variant vorig = d[name]; int found=-1; for(int i=0;iget_button_count(1);i++) { @@ -2065,7 +2151,7 @@ void PropertyEditor::_refresh_item(TreeItem *p_item) { } } - bool changed = ! (v==vorig); + bool changed = _is_property_different(v,vorig,usage); if ((found!=-1)!=changed) { @@ -2326,7 +2412,8 @@ void PropertyEditor::update_tree() { d["type"]=(int)p.type; d["hint"]=(int)p.hint; d["hint_text"]=p.hint_string; - + d["usage"]=(int)p.usage; + item->set_metadata( 0, d ); item->set_metadata( 1, p.name ); @@ -2777,14 +2864,17 @@ void PropertyEditor::update_tree() { } } - if (get_instanced_node()) { + if (_might_be_in_instance()) { - Dictionary d = get_instanced_node()->get_instance_state(); - if (d.has(p.name)) { + Variant vorig; + Dictionary d=item->get_metadata(0); + int usage = d.has("usage")?int(int(d["usage"])&(PROPERTY_USAGE_STORE_IF_NONONE|PROPERTY_USAGE_STORE_IF_NONZERO)):0; + if (_get_instanced_node_original_property(p.name,vorig) || usage) { Variant v = obj->get(p.name); - Variant vorig = d[p.name]; - if (! (v==vorig)) { + + if (_is_property_different(v,vorig,usage)) { + //print_line("FOR "+String(p.name)+" RELOAD WITH: "+String(v)+"("+Variant::get_type_name(v.get_type())+")=="+String(vorig)+"("+Variant::get_type_name(vorig.get_type())+")"); item->add_button(1,get_icon("Reload","EditorIcons"),3); } } @@ -3081,17 +3171,18 @@ void PropertyEditor::_edit_button(Object *p_item, int p_column, int p_button) { call_deferred("_set_range_def",ti,prop,ti->get_range(p_column)+1.0); } else if (p_button==3) { - if (!get_instanced_node()) + if (!_might_be_in_instance()) return; if (!d.has("name")) return; String prop=d["name"]; - Dictionary d2 = get_instanced_node()->get_instance_state(); - if (d2.has(prop)) { + Variant vorig; - _edit_set(prop,d2[prop]); + if (_get_instanced_node_original_property(prop,vorig)) { + + _edit_set(prop,vorig); } } else { diff --git a/tools/editor/property_editor.h b/tools/editor/property_editor.h index 36ecc794ed8..9686bf0bd78 100644 --- a/tools/editor/property_editor.h +++ b/tools/editor/property_editor.h @@ -121,6 +121,7 @@ class CustomPropertyEditor : public Popup { void show_value_editors(int p_amount); void config_value_editors(int p_amount, int p_columns,int p_label_w,const List& p_strings); void config_action_buttons(const List& p_strings); + protected: void _notification(int p_what); @@ -184,13 +185,17 @@ class PropertyEditor : public Control { virtual void _changed_callback(Object *p_changed,const char * p_what); virtual void _changed_callbacks(Object *p_changed,const String& p_callback); + void _edit_button(Object *p_item, int p_column, int p_button); void _node_removed(Node *p_node); void _edit_set(const String& p_name, const Variant& p_value); void _draw_flags(Object *ti,const Rect2& p_rect); - Node *get_instanced_node(); + bool _might_be_in_instance(); + bool _get_instanced_node_original_property(const StringName& p_prop,Variant& value); + bool _is_property_different(const Variant& p_current, const Variant& p_orig,int p_usage=0); + void _refresh_item(TreeItem *p_item); void _set_range_def(Object *p_item, String prop, float p_frame); diff --git a/tools/editor/reparent_dialog.cpp b/tools/editor/reparent_dialog.cpp index 6d0c5b867ea..78ba47d54bd 100644 --- a/tools/editor/reparent_dialog.cpp +++ b/tools/editor/reparent_dialog.cpp @@ -103,6 +103,7 @@ ReparentDialog::ReparentDialog() { add_child(node_only); node_only->hide(); + tree->set_show_enabled_subscene(true); //vbc->add_margin_child("Options:",node_only);; diff --git a/tools/editor/scene_tree_dock.cpp b/tools/editor/scene_tree_dock.cpp index 276f2dea33a..920ab599e91 100644 --- a/tools/editor/scene_tree_dock.cpp +++ b/tools/editor/scene_tree_dock.cpp @@ -36,8 +36,8 @@ #include "script_editor_debugger.h" #include "tools/editor/plugins/script_editor_plugin.h" #include "multi_node_edit.h" -void SceneTreeDock::_unhandled_key_input(InputEvent p_event) { +void SceneTreeDock::_unhandled_key_input(InputEvent p_event) { uint32_t sc = p_event.key.get_scancode_with_modifiers(); if (!p_event.key.pressed || p_event.key.echo) @@ -71,7 +71,7 @@ Node* SceneTreeDock::instance(const String& p_file) { Node*instanced_scene=NULL; Ref sdata = ResourceLoader::load(p_file); if (sdata.is_valid()) - instanced_scene=sdata->instance(); + instanced_scene=sdata->instance(true); if (!instanced_scene) { @@ -96,7 +96,7 @@ Node* SceneTreeDock::instance(const String& p_file) { } } - instanced_scene->generate_instance_state(); + //instanced_scene->generate_instance_state(); instanced_scene->set_filename( Globals::get_singleton()->localize_path(p_file) ); editor_data->get_undo_redo().create_action("Instance Scene"); @@ -158,8 +158,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { case TOOL_NEW: { - if (!_validate_no_foreign()) - break; + //if (!_validate_no_foreign()) + // break; create_dialog->popup_centered_ratio(); } break; case TOOL_INSTANCE: { @@ -176,8 +176,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { break; } - if (!_validate_no_foreign()) - break; + //if (!_validate_no_foreign()) + // break; file->set_mode(EditorFileDialog::MODE_OPEN_FILE); List extensions; @@ -202,8 +202,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (!current) break; - if (!_validate_no_foreign()) - break; + //if (!_validate_no_foreign()) + // break; connect_dialog->popup_centered_ratio(); connect_dialog->set_node(current); @@ -213,8 +213,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { Node *current = scene_tree->get_selected(); if (!current) break; - if (!_validate_no_foreign()) - break; + //if (!_validate_no_foreign()) + // break; groups_editor->set_current(current); groups_editor->popup_centered_ratio(); } break; @@ -224,8 +224,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (!selected) break; - if (!_validate_no_foreign()) - break; + //if (!_validate_no_foreign()) + // break; Ref