diff options
55 files changed, 1594 insertions, 3384 deletions
diff --git a/.gitignore b/.gitignore index e1941b18df..f3a4e9714c 100644 --- a/.gitignore +++ b/.gitignore @@ -37,17 +37,10 @@ platform/android/java/.gradle platform/android/java/.gradletasknamecache platform/android/java/local.properties platform/android/java/project.properties +platform/android/java/build.gradle platform/android/java/AndroidManifest.xml -platform/android/java/bin/* platform/android/java/libs/* -platform/android/java/gen/* platform/android/java/assets -platform/android/libs/apk_expansion/bin/* -platform/android/libs/apk_expansion/gen/* -platform/android/libs/google_play_services/bin/* -platform/android/libs/google_play_services/gen/* -platform/android/libs/play_licensing/bin/* -platform/android/libs/play_licensing/gen/* # General c++ generated files *.lib diff --git a/SConstruct b/SConstruct index 01b1f2ce8e..d168820f66 100644 --- a/SConstruct +++ b/SConstruct @@ -73,6 +73,7 @@ env_base.android_java_dirs=[] env_base.android_res_dirs=[] env_base.android_aidl_dirs=[] env_base.android_jni_dirs=[] +env_base.android_default_config=[] env_base.android_manifest_chunk="" env_base.android_permission_chunk="" env_base.android_appattributes_chunk="" @@ -88,6 +89,7 @@ env_base.__class__.android_add_java_dir=methods.android_add_java_dir env_base.__class__.android_add_res_dir=methods.android_add_res_dir env_base.__class__.android_add_aidl_dir=methods.android_add_aidl_dir env_base.__class__.android_add_jni_dir=methods.android_add_jni_dir +env_base.__class__.android_add_default_config=methods.android_add_default_config env_base.__class__.android_add_to_manifest = methods.android_add_to_manifest env_base.__class__.android_add_to_permissions = methods.android_add_to_permissions env_base.__class__.android_add_to_attributes = methods.android_add_to_attributes diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index ace7e7c7b7..6edc292b62 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -435,6 +435,18 @@ String _OS::get_locale() const { return OS::get_singleton()->get_locale(); } +String _OS::get_latin_keyboard_variant() const { + switch( OS::get_singleton()->get_latin_keyboard_variant() ) { + case OS::LATIN_KEYBOARD_QWERTY: return "QWERTY"; + case OS::LATIN_KEYBOARD_QWERTZ: return "QWERTZ"; + case OS::LATIN_KEYBOARD_AZERTY: return "AZERTY"; + case OS::LATIN_KEYBOARD_QZERTY: return "QZERTY"; + case OS::LATIN_KEYBOARD_DVORAK: return "DVORAK"; + case OS::LATIN_KEYBOARD_NEO : return "NEO"; + default: return "ERROR"; + } +} + String _OS::get_model_name() const { return OS::get_singleton()->get_model_name(); @@ -1097,6 +1109,7 @@ void _OS::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_ticks_msec"),&_OS::get_ticks_msec); ObjectTypeDB::bind_method(_MD("get_splash_tick_msec"),&_OS::get_splash_tick_msec); ObjectTypeDB::bind_method(_MD("get_locale"),&_OS::get_locale); + ObjectTypeDB::bind_method(_MD("get_latin_keyboard_variant"),&_OS::get_latin_keyboard_variant); ObjectTypeDB::bind_method(_MD("get_model_name"),&_OS::get_model_name); ObjectTypeDB::bind_method(_MD("get_custom_level"),&_OS::get_custom_level); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 856d942d02..5bd427578a 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -192,6 +192,8 @@ public: Vector<String> get_cmdline_args(); String get_locale() const; + String get_latin_keyboard_variant() const; + String get_model_name() const; MainLoop *get_main_loop() const; diff --git a/core/os/input.cpp b/core/os/input.cpp index dacddc0928..efbae57950 100644 --- a/core/os/input.cpp +++ b/core/os/input.cpp @@ -59,6 +59,7 @@ void Input::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_joy_axis","device","axis"),&Input::get_joy_axis); ObjectTypeDB::bind_method(_MD("get_joy_name","device"),&Input::get_joy_name); ObjectTypeDB::bind_method(_MD("get_joy_guid","device"),&Input::get_joy_guid); + ObjectTypeDB::bind_method(_MD("get_connected_joysticks"),&Input::get_connected_joysticks); ObjectTypeDB::bind_method(_MD("get_joy_vibration_strength", "device"), &Input::get_joy_vibration_strength); ObjectTypeDB::bind_method(_MD("get_joy_vibration_duration", "device"), &Input::get_joy_vibration_duration); ObjectTypeDB::bind_method(_MD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration, DEFVAL(0)); diff --git a/core/os/input.h b/core/os/input.h index fa2cef5467..d11703470b 100644 --- a/core/os/input.h +++ b/core/os/input.h @@ -62,6 +62,7 @@ public: virtual float get_joy_axis(int p_device,int p_axis)=0; virtual String get_joy_name(int p_idx)=0; + virtual Array get_connected_joysticks()=0; virtual void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid)=0; virtual void add_joy_mapping(String p_mapping, bool p_update_existing=false)=0; virtual void remove_joy_mapping(String p_guid)=0; diff --git a/core/variant_op.cpp b/core/variant_op.cpp index e549161de9..9182dcde1a 100644 --- a/core/variant_op.cpp +++ b/core/variant_op.cpp @@ -2911,6 +2911,14 @@ bool Variant::iter_init(Variant& r_iter,bool &valid) const { return ret; } break; + case STRING: { + + const String *str=reinterpret_cast<const String*>(_data._mem); + if (str->empty()) + return false; + r_iter = 0; + return true; + } break; case DICTIONARY: { const Dictionary *dic=reinterpret_cast<const Dictionary*>(_data._mem); @@ -3028,6 +3036,17 @@ bool Variant::iter_next(Variant& r_iter,bool &valid) const { return ret; } break; + + case STRING: { + + const String *str=reinterpret_cast<const String*>(_data._mem); + int idx = r_iter; + idx++; + if (idx >= str->size()) + return false; + r_iter = idx; + return true; + } break; case DICTIONARY: { const Dictionary *dic=reinterpret_cast<const Dictionary*>(_data._mem); @@ -3158,6 +3177,11 @@ Variant Variant::iter_get(const Variant& r_iter,bool &r_valid) const { return ret; } break; + case STRING: { + + const String *str=reinterpret_cast<const String*>(_data._mem); + return str->substr(r_iter,1); + } break; case DICTIONARY: { return r_iter; //iterator is the same as the key diff --git a/doc/base/classes.xml b/doc/base/classes.xml index cffbd68939..b5384cfc2b 100644 --- a/doc/base/classes.xml +++ b/doc/base/classes.xml @@ -6676,6 +6676,17 @@ Force the camera to update scroll immediately. </description> </method> + <method name="reset_smoothing"> + <description> + Set the camera's position immediately to its current smoothing destination. + This has no effect if smoothing is disabled. + </description> + </method> + <method name="align"> + <description> + Align the camera to the tracked node + </description> + </method> <method name="get_anchor_mode" qualifiers="const"> <return type="int"> </return> @@ -15748,6 +15759,13 @@ If the device has an accelerometer, this will return the movement. </description> </method> + <method name="get_connected_joysticks"> + <return type="Array"> + </return> + <description> + Returns an [Array] containing the device IDs of all currently connected joysticks. + </description> + </method> <method name="get_joy_axis"> <return type="float"> </return> @@ -21677,6 +21695,12 @@ <description> </description> </method> + <method name="is_displayed_folded" qualifiers="const"> + <return type="bool"> + </return> + <description> + </description> + </method> <method name="is_fixed_processing" qualifiers="const"> <return type="bool"> </return> @@ -21802,6 +21826,12 @@ <description> </description> </method> + <method name="set_display_folded"> + <argument index="0" name="fold" type="bool"> + </argument> + <description> + </description> + </method> <method name="set_filename"> <argument index="0" name="filename" type="String"> </argument> @@ -31551,6 +31581,13 @@ <description> </description> </method> + <method name="get_text"> + <return type="String"> + </return> + <description> + Returns the raw text, stripping out the formatting information. + </description> + </method> <method name="get_total_character_count" qualifiers="const"> <return type="int"> </return> diff --git a/main/input_default.cpp b/main/input_default.cpp index 4fcb450bce..945898f1f3 100644 --- a/main/input_default.cpp +++ b/main/input_default.cpp @@ -30,6 +30,7 @@ #include "servers/visual_server.h" #include "os/os.h" #include "input_map.h" +#include "scene/resources/texture.h" void InputDefault::SpeedTrack::update(const Vector2& p_delta_p) { @@ -101,7 +102,7 @@ bool InputDefault::is_action_pressed(const StringName& p_action) { const List<InputEvent> *alist = InputMap::get_singleton()->get_action_list(p_action); if (!alist) - return NULL; + return false; for (const List<InputEvent>::Element *E=alist->front();E;E=E->next()) { @@ -220,18 +221,18 @@ void InputDefault::joy_connection_changed(int p_idx, bool p_connected, String p_ }; }; js.uid = uidname; - //printf("looking for mappings for guid %ls\n", uidname.c_str()); + js.connected = true; int mapping = fallback_mapping; for (int i=0; i < map_db.size(); i++) { if (js.uid == map_db[i].uid) { mapping = i; js.name = map_db[i].name; - //printf("found mapping\n"); }; }; js.mapping = mapping; } else { + js.connected = false; for (int i = 0; i < JOY_BUTTON_MAX; i++) { if (i < JOY_AXIS_MAX) @@ -463,9 +464,11 @@ void InputDefault::set_custom_mouse_cursor(const RES& p_cursor,const Vector2& p_ set_mouse_mode(MOUSE_MODE_VISIBLE); VisualServer::get_singleton()->cursor_set_visible(false); } else { + Ref<AtlasTexture> atex = custom_cursor; + Rect2 region = atex.is_valid() ? atex->get_region() : Rect2(); set_mouse_mode(MOUSE_MODE_HIDDEN); VisualServer::get_singleton()->cursor_set_visible(true); - VisualServer::get_singleton()->cursor_set_texture(custom_cursor->get_rid(),p_hotspot); + VisualServer::get_singleton()->cursor_set_texture(custom_cursor->get_rid(),p_hotspot, 0, region); VisualServer::get_singleton()->cursor_set_pos(get_mouse_pos()); } } @@ -1039,3 +1042,15 @@ bool InputDefault::is_joy_mapped(int p_device) { String InputDefault::get_joy_guid_remapped(int p_device) const { return joy_names[p_device].uid; } + +Array InputDefault::get_connected_joysticks() { + Array ret; + Map<int, Joystick>::Element *elem = joy_names.front(); + while (elem) { + if (elem->get().connected) { + ret.push_back(elem->key()); + } + elem = elem->next(); + } + return ret; +} diff --git a/main/input_default.h b/main/input_default.h index c7fef8374c..644af15e3b 100644 --- a/main/input_default.h +++ b/main/input_default.h @@ -75,6 +75,7 @@ class InputDefault : public Input { struct Joystick { StringName name; StringName uid; + bool connected; bool last_buttons[JOY_BUTTON_MAX + 19]; //apparently SDL specifies 35 possible buttons on android float last_axis[JOY_AXIS_MAX]; float filter; @@ -93,6 +94,7 @@ class InputDefault : public Input { last_buttons[i] = false; } + connected = false; last_hat = HAT_MASK_CENTER; filter = 0.01f; mapping = -1; @@ -168,6 +170,7 @@ public: virtual float get_joy_axis(int p_device,int p_axis); String get_joy_name(int p_idx); + virtual Array get_connected_joysticks(); virtual Vector2 get_joy_vibration_strength(int p_device); virtual float get_joy_vibration_duration(int p_device); virtual uint64_t get_joy_vibration_timestamp(int p_device); diff --git a/methods.py b/methods.py index 7128b334ec..74c282b8cf 100755 --- a/methods.py +++ b/methods.py @@ -1326,7 +1326,9 @@ def android_add_aidl_dir(self,subpath): def android_add_jni_dir(self,subpath): base_path = self.Dir(".").abspath+"/modules/"+self.current_module+"/"+subpath self.android_jni_dirs.append(base_path) - +def android_add_default_config(self,config): + self.android_default_config.append(config) + def android_add_to_manifest(self,file): base_path = self.Dir(".").abspath+"/modules/"+self.current_module+"/"+file f = open(base_path,"rb") diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp index e87bd99e4f..6a7cd5eb8c 100644 --- a/modules/gdscript/gd_parser.cpp +++ b/modules/gdscript/gd_parser.cpp @@ -1536,6 +1536,10 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { return; } tokenizer->advance(); + if(tokenizer->get_token()==GDTokenizer::TK_SEMICOLON) { + // Ignore semicolon after 'pass' + tokenizer->advance(); + } } break; case GDTokenizer::TK_PR_VAR: { //variale declaration and (eventual) initialization diff --git a/platform/android/SCsub b/platform/android/SCsub index c8feac8690..60bb4bd613 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -79,6 +79,11 @@ for x in env.android_jni_dirs: gradle_asset_dirs_text="" +gradle_default_config_text="" + +for x in env.android_default_config: + gradle_default_config_text+=x+"\n\t\t" + gradle_text = gradle_text.replace("$$GRADLE_REPOSITORY_URLS$$",gradle_maven_repos_text) gradle_text = gradle_text.replace("$$GRADLE_DEPENDENCIES$$",gradle_maven_dependencies_text) gradle_text = gradle_text.replace("$$GRADLE_JAVA_DIRS$$",gradle_java_dirs_text) @@ -86,6 +91,7 @@ gradle_text = gradle_text.replace("$$GRADLE_RES_DIRS$$",gradle_res_dirs_text) gradle_text = gradle_text.replace("$$GRADLE_ASSET_DIRS$$",gradle_asset_dirs_text) gradle_text = gradle_text.replace("$$GRADLE_AIDL_DIRS$$",gradle_aidl_dirs_text) gradle_text = gradle_text.replace("$$GRADLE_JNI_DIRS$$",gradle_jni_dirs_text) +gradle_text = gradle_text.replace("$$GRADLE_DEFAULT_CONFIG$$",gradle_default_config_text) gradle_baseout.write( gradle_text ) @@ -103,4 +109,22 @@ pp_baseout.write( manifest ) env_android.SharedLibrary("#bin/libgodot",[android_objects],SHLIBSUFFIX=env["SHLIBSUFFIX"]) -#env.Command('#bin/libgodot_android.so', '#platform/android/libgodot_android.so', Copy('bin/libgodot_android.so', 'platform/android/libgodot_android.so')) + +lib_arch_dir = '' +if env['android_arch'] == 'armv6': + lib_arch_dir = 'armeabi' +elif env['android_arch'] == 'armv7': + lib_arch_dir = 'armeabi-v7a' +elif env['android_arch'] == 'x86': + lib_arch_dir = 'x86' +else: + print 'WARN: Architecture not suitable for embedding into APK; keeping .so at \\bin' + +if lib_arch_dir != '': + if env['target'] == 'release': + lib_type_dir = 'release' + else: # release_debug, debug + lib_type_dir = 'debug' + + out_dir = '#platform/android/java/libs/'+lib_type_dir+'/'+lib_arch_dir + env_android.Command(out_dir+'/libgodot_android.so', '#bin/libgodot'+env['SHLIBSUFFIX'], Move("$TARGET", "$SOURCE")) diff --git a/platform/android/build.gradle.template b/platform/android/build.gradle.template index 1e1461ef29..24951b921b 100644 --- a/platform/android/build.gradle.template +++ b/platform/android/build.gradle.template @@ -3,7 +3,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.3.1' + classpath 'com.android.tools.build:gradle:2.1.0' } } @@ -39,6 +39,12 @@ android { defaultConfig { minSdkVersion 14 targetSdkVersion 23 + $$GRADLE_DEFAULT_CONFIG$$ + } + // Both signing and zip-aligning will be done at export time + buildTypes.all { buildType -> + buildType.zipAlignEnabled false + buildType.signingConfig null } sourceSets { main { @@ -65,8 +71,17 @@ android { $$GRADLE_JNI_DIRS$$ ] } - + debug.jniLibs.srcDirs = [ + 'libs/debug' + $$GRADLE_JNI_DIRS$$ + ] + release.jniLibs.srcDirs = [ + 'libs/release' + $$GRADLE_JNI_DIRS$$ + ] + } + applicationVariants.all { variant -> + // ApplicationVariant is undocumented, but this method is widely used; may break with another version of the Android Gradle plugin + variant.outputs.get(0).setOutputFile(new File("${projectDir}/../../../bin", "android_${variant.name}.apk")) } - - } diff --git a/platform/android/detect.py b/platform/android/detect.py index ba6b73a89f..6fd0b81d91 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -23,7 +23,7 @@ def get_opts(): ('ANDROID_NDK_ROOT', 'the path to Android NDK', os.environ.get("ANDROID_NDK_ROOT", 0)), ('NDK_TARGET', 'toolchain to use for the NDK',os.environ.get("NDK_TARGET", "arm-linux-androideabi-4.9")), ('NDK_TARGET_X86', 'toolchain to use for the NDK x86',os.environ.get("NDK_TARGET_X86", "x86-4.9")), - ('ndk_platform', 'compile for platform: (android-<api> , example: android-15)',"android-15"), + ('ndk_platform', 'compile for platform: (android-<api> , example: android-14)',"android-14"), ('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") diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 872f047c95..83f7292716 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -1166,7 +1166,7 @@ Error EditorExportPlatformAndroid::export_project(const String& p_path, bool p_d skip=true; } - if (file=="lib/armeabi/libgodot_android.so" && !export_arm) { + if (file.match("lib/armeabi*/libgodot_android.so") && !export_arm) { skip=true; } diff --git a/platform/android/java/gradle/wrapper/gradle-wrapper.properties b/platform/android/java/gradle/wrapper/gradle-wrapper.properties index 0c71e760dc..d57051703e 100644 --- a/platform/android/java/gradle/wrapper/gradle-wrapper.properties +++ b/platform/android/java/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 89de969cff..b0a50ca4b8 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -2178,6 +2178,68 @@ String OS_Windows::get_locale() const { return "en"; } + +OS::LatinKeyboardVariant OS_Windows::get_latin_keyboard_variant() const { + + unsigned long azerty[] = { + 0x00020401, // Arabic (102) AZERTY + 0x0001080c, // Belgian (Comma) + 0x0000080c, // Belgian French + 0x0000040c, // French + 0 // <--- STOP MARK + }; + unsigned long qwertz[] = { + 0x0000041a, // Croation + 0x00000405, // Czech + 0x00000407, // German + 0x00010407, // German (IBM) + 0x0000040e, // Hungarian + 0x0000046e, // Luxembourgish + 0x00010415, // Polish (214) + 0x00000418, // Romanian (Legacy) + 0x0000081a, // Serbian (Latin) + 0x0000041b, // Slovak + 0x00000424, // Slovenian + 0x0001042e, // Sorbian Extended + 0x0002042e, // Sorbian Standard + 0x0000042e, // Sorbian Standard (Legacy) + 0x0000100c, // Swiss French + 0x00000807, // Swiss German + 0 // <--- STOP MARK + }; + unsigned long dvorak[] = { + 0x00010409, // US-Dvorak + 0x00030409, // US-Dvorak for left hand + 0x00040409, // US-Dvorak for right hand + 0 // <--- STOP MARK + }; + + char name[ KL_NAMELENGTH + 1 ]; name[0] = 0; + GetKeyboardLayoutNameA( name ); + + unsigned long hex = strtoul(name, NULL, 16); + + int i=0; + while( azerty[i] != 0 ) { + if (azerty[i] == hex) return LATIN_KEYBOARD_AZERTY; + i++; + } + + i = 0; + while( qwertz[i] != 0 ) { + if (qwertz[i] == hex) return LATIN_KEYBOARD_QWERTZ; + i++; + } + + i = 0; + while( dvorak[i] != 0 ) { + if (dvorak[i] == hex) return LATIN_KEYBOARD_DVORAK; + i++; + } + + return LATIN_KEYBOARD_QWERTY; +} + void OS_Windows::release_rendering_thread() { gl_context->release_current(); diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index d2249bf352..5acb300c0f 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -266,6 +266,7 @@ public: virtual String get_executable_path() const; virtual String get_locale() const; + virtual LatinKeyboardVariant get_latin_keyboard_variant() const; virtual void move_window_to_foreground(); virtual String get_data_dir() const; diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index 27e07a35be..f98a50e3e0 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -408,6 +408,35 @@ void Camera2D::force_update_scroll() { _update_scroll(); } +void Camera2D::reset_smoothing() { + + smoothed_camera_pos = camera_pos; + _update_scroll(); +} + +void Camera2D::align() { + + Size2 screen_size = get_viewport_rect().size; + screen_size=get_viewport_rect().size; + Point2 current_camera_pos = get_global_transform().get_origin(); + if (anchor_mode==ANCHOR_MODE_DRAG_CENTER) { + if (h_ofs<0) { + camera_pos.x = current_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_RIGHT] * h_ofs; + } else { + camera_pos.x = current_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_LEFT] * h_ofs; + } + if (v_ofs<0) { + camera_pos.y = current_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_TOP] * v_ofs; + } else { + camera_pos.y = current_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_BOTTOM] * v_ofs; + } + } else if (anchor_mode==ANCHOR_MODE_FIXED_TOP_LEFT){ + + camera_pos=current_camera_pos; + } + + _update_scroll(); +} void Camera2D::set_follow_smoothing(float p_speed) { @@ -543,6 +572,8 @@ void Camera2D::_bind_methods() { ObjectTypeDB::bind_method(_MD("is_follow_smoothing_enabled"),&Camera2D::is_follow_smoothing_enabled); ObjectTypeDB::bind_method(_MD("force_update_scroll"),&Camera2D::force_update_scroll); + ObjectTypeDB::bind_method(_MD("reset_smoothing"),&Camera2D::reset_smoothing); + ObjectTypeDB::bind_method(_MD("align"),&Camera2D::align); ObjectTypeDB::bind_method(_MD("_set_old_smoothing","follow_smoothing"),&Camera2D::_set_old_smoothing); diff --git a/scene/2d/camera_2d.h b/scene/2d/camera_2d.h index 22e5bc382a..b3f55d798d 100644 --- a/scene/2d/camera_2d.h +++ b/scene/2d/camera_2d.h @@ -128,6 +128,8 @@ public: Vector2 get_camera_pos() const; void force_update_scroll(); + void reset_smoothing(); + void align(); Camera2D(); }; diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 1f16b36466..1a4f88c30e 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -222,6 +222,10 @@ void TileMap::_fix_cell_transform(Matrix32& xform,const Cell& p_cell, const Vect Size2 s=p_sc; Vector2 offset = p_offset; + + if (tile_origin==TILE_ORIGIN_BOTTOM_LEFT) + offset.y+=cell_size.y; + if (s.y > s.x) { if ((p_cell.flip_h && (p_cell.flip_v || p_cell.transpose)) || (p_cell.flip_v && !p_cell.transpose)) @@ -240,7 +244,7 @@ void TileMap::_fix_cell_transform(Matrix32& xform,const Cell& p_cell, const Vect if (p_cell.flip_h) { xform.elements[0].x=-xform.elements[0].x; xform.elements[1].x=-xform.elements[1].x; - if (tile_origin==TILE_ORIGIN_TOP_LEFT) + if (tile_origin==TILE_ORIGIN_TOP_LEFT || tile_origin==TILE_ORIGIN_BOTTOM_LEFT) offset.x=s.x-offset.x; } if (p_cell.flip_v) { @@ -248,6 +252,12 @@ void TileMap::_fix_cell_transform(Matrix32& xform,const Cell& p_cell, const Vect xform.elements[1].y=-xform.elements[1].y; if (tile_origin==TILE_ORIGIN_TOP_LEFT) offset.y=s.y-offset.y; + else if (tile_origin==TILE_ORIGIN_BOTTOM_LEFT) { + if(p_cell.transpose) + offset.y+=s.y; + else + offset.y-=s.y; + } } xform.elements[2].x+=offset.x; xform.elements[2].y+=offset.y; @@ -411,6 +421,24 @@ void TileMap::_update_dirty_quadrants() { if (tile_origin==TILE_ORIGIN_TOP_LEFT) { rect.pos+=tile_ofs; + + } else if (tile_origin==TILE_ORIGIN_BOTTOM_LEFT) { + + rect.pos+=tile_ofs; + + if(c.transpose) + { + if(c.flip_h) + rect.pos.x-=cell_size.x; + else + rect.pos.x+=cell_size.x; + } else { + if(c.flip_v) + rect.pos.y-=cell_size.y; + else + rect.pos.y+=cell_size.y; + } + } else if (tile_origin==TILE_ORIGIN_CENTER) { rect.pos+=tcenter; @@ -584,6 +612,9 @@ Map<TileMap::PosKey,TileMap::Quadrant>::Element *TileMap::_create_quadrant(const q.pos+=get_cell_draw_offset(); if (tile_origin==TILE_ORIGIN_CENTER) q.pos+=cell_size/2; + else if (tile_origin==TILE_ORIGIN_BOTTOM_LEFT) + q.pos.y+=cell_size.y; + xform.set_origin( q.pos ); // q.canvas_item = VisualServer::get_singleton()->canvas_item_create(); @@ -1242,7 +1273,7 @@ void TileMap::_bind_methods() { ADD_PROPERTY( PropertyInfo(Variant::INT,"cell/quadrant_size",PROPERTY_HINT_RANGE,"1,128,1"),_SCS("set_quadrant_size"),_SCS("get_quadrant_size")); ADD_PROPERTY( PropertyInfo(Variant::MATRIX32,"cell/custom_transform"),_SCS("set_custom_transform"),_SCS("get_custom_transform")); ADD_PROPERTY( PropertyInfo(Variant::INT,"cell/half_offset",PROPERTY_HINT_ENUM,"Offset X,Offset Y,Disabled"),_SCS("set_half_offset"),_SCS("get_half_offset")); - ADD_PROPERTY( PropertyInfo(Variant::INT,"cell/tile_origin",PROPERTY_HINT_ENUM,"Top Left,Center"),_SCS("set_tile_origin"),_SCS("get_tile_origin")); + ADD_PROPERTY( PropertyInfo(Variant::INT,"cell/tile_origin",PROPERTY_HINT_ENUM,"Top Left,Center,Bottom Left"),_SCS("set_tile_origin"),_SCS("get_tile_origin")); ADD_PROPERTY( PropertyInfo(Variant::BOOL,"cell/y_sort"),_SCS("set_y_sort_mode"),_SCS("is_y_sort_mode_enabled")); ADD_PROPERTY( PropertyInfo(Variant::BOOL,"collision/use_kinematic",PROPERTY_HINT_NONE,""),_SCS("set_collision_use_kinematic"),_SCS("get_collision_use_kinematic")); ADD_PROPERTY( PropertyInfo(Variant::REAL,"collision/friction",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_collision_friction"),_SCS("get_collision_friction")); @@ -1264,6 +1295,7 @@ void TileMap::_bind_methods() { BIND_CONSTANT( HALF_OFFSET_DISABLED ); BIND_CONSTANT( TILE_ORIGIN_TOP_LEFT ); BIND_CONSTANT( TILE_ORIGIN_CENTER ); + BIND_CONSTANT( TILE_ORIGIN_BOTTOM_LEFT ); } diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index cec5ac0a1b..b48fdde43f 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -54,7 +54,8 @@ public: enum TileOrigin { TILE_ORIGIN_TOP_LEFT, - TILE_ORIGIN_CENTER + TILE_ORIGIN_CENTER, + TILE_ORIGIN_BOTTOM_LEFT }; diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp index 628edf09de..9488ae37a8 100644 --- a/scene/animation/animation_tree_player.cpp +++ b/scene/animation/animation_tree_player.cpp @@ -445,8 +445,44 @@ void AnimationTreePlayer::_notification(int p_what) { } +void AnimationTreePlayer::_compute_weights(float *p_fallback_weight, HashMap<NodePath,float> *p_weights, float p_coeff, const HashMap<NodePath,bool> *p_filter, float p_filtered_coeff) { -float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode **r_prev_anim,float p_weight, float p_time, bool p_seek,const HashMap<NodePath,bool> *p_filter, float p_reverse_weight) { + if (p_filter != NULL) { + + List<NodePath> key_list; + p_filter->get_key_list(&key_list); + + for (List<NodePath>::Element *E = key_list.front();E; E=E->next()) { + + if ((*p_filter)[E->get()]) { + + if (p_weights->has(E->get())) { + (*p_weights)[E->get()] *= p_filtered_coeff; + } else { + p_weights->set(E->get(), *p_fallback_weight * p_filtered_coeff); + } + + } else if (p_weights->has(E->get())) { + (*p_weights)[E->get()] *= p_coeff; + } + } + } + + List<NodePath> key_list; + p_weights->get_key_list(&key_list); + + for (List<NodePath>::Element *E = key_list.front();E;E=E->next()) { + if (p_filter == NULL || !p_filter->has(E->get())) { + (*p_weights)[E->get()] *= p_coeff; + } + } + + *p_fallback_weight *= p_coeff; + +} + + +float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode **r_prev_anim, float p_time, bool p_seek, float p_fallback_weight, HashMap<NodePath,float>* p_weights) { ERR_FAIL_COND_V(!node_map.has(p_node), 0); NodeBase *nb=node_map[p_node]; @@ -458,7 +494,16 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode case NODE_OUTPUT: { NodeOut *on = static_cast<NodeOut*>(nb); - return _process_node(on->inputs[0].node,r_prev_anim,p_weight,p_time,p_seek); + + for(TrackMap::Element *E=track_map.front();E;E=E->next()) { + E->get().total_weight = 0; + } + + HashMap<NodePath, float> weights; + + + + return _process_node(on->inputs[0].node,r_prev_anim,p_time,p_seek, p_fallback_weight, &weights); } break; case NODE_ANIMATION: { @@ -494,15 +539,21 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode } an->skip=true; + for (List<AnimationNode::TrackRef>::Element *E=an->tref.front();E;E=E->next()) { NodePath track_path = an->animation->track_get_path(E->get().local_track); - if (p_filter && p_filter->has(track_path)) { - E->get().weight = MAX(0, p_reverse_weight); - } else if(an->filter.has(track_path)) { + if (an->filter.has(track_path) && an->filter[track_path]) { E->get().weight = 0; - E->get().track->skip = true; + E->get().track->total_weight += p_fallback_weight; } else { - E->get().weight=p_weight; + if (p_weights->has(track_path)) { + float weight = (*p_weights)[track_path]; + E->get().weight = weight; + E->get().track->total_weight += weight; + } else { + E->get().weight = p_fallback_weight; + E->get().track->total_weight += p_fallback_weight; + } } if (E->get().weight>CMP_EPSILON) an->skip=false; @@ -531,7 +582,7 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode if (!osn->active) { //make it as if this node doesn't exist, pass input 0 by. - return _process_node(osn->inputs[0].node,r_prev_anim,p_weight,p_time,p_seek,p_filter,p_reverse_weight); + return _process_node(osn->inputs[0].node,r_prev_anim,p_time,p_seek, p_fallback_weight, p_weights); } float os_seek = p_seek; @@ -563,16 +614,14 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode float main_rem; float os_rem; - float os_reverse_weight = p_reverse_weight; - if (!osn->filter.empty()) { - p_filter = &osn->filter; - p_reverse_weight = p_weight; - os_reverse_weight = -1; - } + HashMap<NodePath, float> os_weights(*p_weights); + float os_fallback_weight = p_fallback_weight; + _compute_weights(&p_fallback_weight, p_weights, osn->mix?1.0 : 1.0 - blend, &osn->filter, 1.0); + _compute_weights(&os_fallback_weight, &os_weights, blend, &osn->filter, 0.0); - main_rem = _process_node(osn->inputs[0].node,r_prev_anim,(osn->mix?p_weight:p_weight*(1.0-blend)),p_time,p_seek,p_filter,p_reverse_weight); - os_rem = _process_node(osn->inputs[1].node,r_prev_anim,p_weight*blend,p_time,os_seek,p_filter,os_reverse_weight); + main_rem = _process_node(osn->inputs[0].node,r_prev_anim,p_time,p_seek, p_fallback_weight, p_weights); + os_rem = _process_node(osn->inputs[1].node,r_prev_anim,p_time,os_seek, os_fallback_weight, &os_weights); if (osn->start) { osn->remaining=os_rem; @@ -591,9 +640,11 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode case NODE_MIX: { MixNode *mn = static_cast<MixNode*>(nb); - - float rem = _process_node(mn->inputs[0].node,r_prev_anim,p_weight,p_time,p_seek,p_filter,p_reverse_weight); - _process_node(mn->inputs[1].node,r_prev_anim,p_weight*mn->amount,p_time,p_seek,p_filter,p_reverse_weight); + HashMap<NodePath, float> mn_weights(*p_weights); + float mn_fallback_weight = p_fallback_weight; + _compute_weights(&mn_fallback_weight, &mn_weights, mn->amount); + float rem = _process_node(mn->inputs[0].node,r_prev_anim, p_time,p_seek,p_fallback_weight,p_weights); + _process_node(mn->inputs[1].node,r_prev_anim,p_time,p_seek,mn_fallback_weight,&mn_weights); return rem; } break; @@ -601,16 +652,12 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode Blend2Node *bn = static_cast<Blend2Node*>(nb); - float rem; - if (!bn->filter.empty()) { - - rem = _process_node(bn->inputs[0].node,r_prev_anim,p_weight*(1.0-bn->value),p_time,p_seek,&bn->filter,p_weight); - _process_node(bn->inputs[1].node,r_prev_anim,p_weight*bn->value,p_time,p_seek,&bn->filter,-1); - - } else { - rem = _process_node(bn->inputs[0].node,r_prev_anim,p_weight*(1.0-bn->value),p_time,p_seek,p_filter,p_reverse_weight*(1.0-bn->value)); - _process_node(bn->inputs[1].node,r_prev_anim,p_weight*bn->value,p_time,p_seek,p_filter,p_reverse_weight*bn->value); - } + HashMap<NodePath, float> bn_weights(*p_weights); + float bn_fallback_weight = p_fallback_weight; + _compute_weights(&p_fallback_weight,p_weights, 1.0 - bn->value, &bn->filter, 1.0); + _compute_weights(&bn_fallback_weight,&bn_weights, bn->value, &bn->filter, 0.0); + float rem = _process_node(bn->inputs[0].node,r_prev_anim,p_time,p_seek,p_fallback_weight,p_weights); + _process_node(bn->inputs[1].node,r_prev_anim,p_time,p_seek,bn_fallback_weight,&bn_weights); return rem; } break; @@ -629,19 +676,39 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode upper_blend = bn->value; } - rem = _process_node(bn->inputs[1].node,r_prev_anim,p_weight*blend,p_time,p_seek,p_filter,p_reverse_weight*blend); - _process_node(bn->inputs[2].node,r_prev_anim,p_weight*upper_blend,p_time,p_seek,p_filter,p_reverse_weight*upper_blend); - _process_node(bn->inputs[0].node,r_prev_anim,p_weight*lower_blend,p_time,p_seek,p_filter,p_reverse_weight*lower_blend); + HashMap<NodePath, float> upper_weights(*p_weights); + float upper_fallback_weight = p_fallback_weight; + HashMap<NodePath, float> lower_weights(*p_weights); + float lower_fallback_weight = p_fallback_weight; + _compute_weights(&upper_fallback_weight,&upper_weights, upper_blend); + _compute_weights(&p_fallback_weight,p_weights, blend); + _compute_weights(&lower_fallback_weight,&lower_weights, lower_blend); + + rem = _process_node(bn->inputs[1].node,r_prev_anim,p_time,p_seek,p_fallback_weight,p_weights); + _process_node(bn->inputs[0].node,r_prev_anim,p_time,p_seek,lower_fallback_weight,&lower_weights); + _process_node(bn->inputs[2].node,r_prev_anim,p_time,p_seek,upper_fallback_weight,&upper_weights); return rem; } break; case NODE_BLEND4: { Blend4Node *bn = static_cast<Blend4Node*>(nb); - float rem = _process_node(bn->inputs[0].node,r_prev_anim,p_weight*(1.0-bn->value.x),p_time,p_seek,p_filter,p_reverse_weight*(1.0-bn->value.x)); - _process_node(bn->inputs[1].node,r_prev_anim,p_weight*bn->value.x,p_time,p_seek,p_filter,p_reverse_weight*bn->value.x); - float rem2 = _process_node(bn->inputs[2].node,r_prev_anim,p_weight*(1.0-bn->value.y),p_time,p_seek,p_filter,p_reverse_weight*(1.0-bn->value.y)); - _process_node(bn->inputs[3].node,r_prev_anim,p_weight*bn->value.y,p_time,p_seek,p_filter,p_reverse_weight*bn->value.y); + HashMap<NodePath, float> weights1(*p_weights); + float fallback_weight1 = p_fallback_weight; + HashMap<NodePath, float> weights2(*p_weights); + float fallback_weight2 = p_fallback_weight; + HashMap<NodePath, float> weights3(*p_weights); + float fallback_weight3 = p_fallback_weight; + + _compute_weights(&p_fallback_weight,p_weights, 1.0-bn->value.x); + _compute_weights(&fallback_weight1,&weights1, bn->value.x); + _compute_weights(&fallback_weight2,&weights2, 1.0-bn->value.y); + _compute_weights(&fallback_weight3,&weights3, bn->value.y); + + float rem = _process_node(bn->inputs[0].node,r_prev_anim,p_time,p_seek,p_fallback_weight,p_weights); + _process_node(bn->inputs[1].node,r_prev_anim,p_time,p_seek,fallback_weight1,&weights1); + float rem2 = _process_node(bn->inputs[2].node,r_prev_anim,p_time,p_seek,fallback_weight2,&weights2); + _process_node(bn->inputs[3].node,r_prev_anim,p_time,p_seek,fallback_weight3,&weights3); return MAX(rem,rem2); @@ -650,9 +717,9 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode TimeScaleNode *tsn = static_cast<TimeScaleNode*>(nb); float rem; if (p_seek) - rem = _process_node(tsn->inputs[0].node,r_prev_anim,p_weight,p_time,true,p_filter,p_reverse_weight); + rem = _process_node(tsn->inputs[0].node,r_prev_anim,p_time,true,p_fallback_weight,p_weights); else - rem = _process_node(tsn->inputs[0].node,r_prev_anim,p_weight,p_time*tsn->scale,false,p_filter,p_reverse_weight); + rem = _process_node(tsn->inputs[0].node,r_prev_anim,p_time*tsn->scale,false,p_fallback_weight,p_weights); if (tsn->scale == 0) return INFINITY; else @@ -669,16 +736,18 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode } tsn->seek_pos=-1; - return _process_node(tsn->inputs[0].node,r_prev_anim,p_weight,p_time,p_seek, p_filter, p_reverse_weight); + return _process_node(tsn->inputs[0].node,r_prev_anim,p_time,p_seek,p_fallback_weight,p_weights); } break; case NODE_TRANSITION: { TransitionNode *tn = static_cast<TransitionNode*>(nb); + HashMap<NodePath, float> prev_weights(*p_weights); + float prev_fallback_weight = p_fallback_weight; if (tn->prev<0) { // process current animation, check for transition - float rem = _process_node(tn->inputs[tn->current].node,r_prev_anim,p_weight,p_time,p_seek,p_filter,p_reverse_weight); + float rem = _process_node(tn->inputs[tn->current].node,r_prev_anim,p_time,p_seek,p_fallback_weight,p_weights); if (p_seek) tn->time=p_time; else @@ -698,22 +767,25 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode float rem; + _compute_weights(&p_fallback_weight,p_weights, 1.0-blend); + _compute_weights(&prev_fallback_weight,&prev_weights, blend); + if (!p_seek && tn->switched) { //just switched, seek to start of current - rem = _process_node(tn->inputs[tn->current].node,r_prev_anim,p_weight*(1.0-blend),0,true,p_filter,p_reverse_weight*(1.0-blend)); + rem = _process_node(tn->inputs[tn->current].node,r_prev_anim,0,true,p_fallback_weight,p_weights); } else { - rem = _process_node(tn->inputs[tn->current].node,r_prev_anim,p_weight*(1.0-blend),p_time,p_seek,p_filter,p_reverse_weight*(1.0-blend)); + rem = _process_node(tn->inputs[tn->current].node,r_prev_anim,p_time,p_seek,p_fallback_weight,p_weights); } tn->switched=false; if (p_seek) { // don't seek prev animation - _process_node(tn->inputs[tn->prev].node,r_prev_anim,p_weight*blend,0,false,p_filter,p_reverse_weight*blend); + _process_node(tn->inputs[tn->prev].node,r_prev_anim,0,false,prev_fallback_weight,&prev_weights); tn->time=p_time; } else { - _process_node(tn->inputs[tn->prev].node,r_prev_anim,p_weight*blend,p_time,false,p_filter,p_reverse_weight*blend); + _process_node(tn->inputs[tn->prev].node,r_prev_anim,p_time,false,prev_fallback_weight,&prev_weights); tn->time+=p_time; tn->prev_xfading-=p_time; if (tn->prev_xfading<0) { @@ -750,10 +822,11 @@ void AnimationTreePlayer::_process_animation(float p_delta) { AnimationNode *prev=NULL; if (reset_request) { - _process_node(out_name,&prev, 1.0, 0, true); + + _process_node(out_name,&prev, 0, true); reset_request=false; } else - _process_node(out_name,&prev, 1.0, p_delta); + _process_node(out_name,&prev, p_delta); if (dirty_caches) { //some animation changed.. ignore this pass @@ -802,7 +875,7 @@ void AnimationTreePlayer::_process_animation(float p_delta) { if (tr.track==NULL || tr.local_track<0 || tr.weight < CMP_EPSILON) continue; - float blend=tr.weight; + float blend=tr.weight / tr.track->total_weight; switch(a->track_get_type(tr.local_track)) { case Animation::TYPE_TRANSFORM: { ///< Transform a node or a bone. @@ -1904,6 +1977,3 @@ AnimationTreePlayer::~AnimationTreePlayer() { node_map.erase( node_map.front() ); } } - - - diff --git a/scene/animation/animation_tree_player.h b/scene/animation/animation_tree_player.h index dae891b5ce..6b5350e9ee 100644 --- a/scene/animation/animation_tree_player.h +++ b/scene/animation/animation_tree_player.h @@ -111,6 +111,7 @@ private: Variant value; bool skip; + float total_weight; }; @@ -273,7 +274,7 @@ private: Map<StringName,NodeBase*> node_map; // return time left to finish animation - float _process_node(const StringName& p_node,AnimationNode **r_prev_anim, float p_weight,float p_step, bool p_seek=false,const HashMap<NodePath,bool> *p_filter=NULL, float p_reverse_weight=0); + float _process_node(const StringName& p_node,AnimationNode **r_prev_anim,float p_step, bool p_seek=false, float p_fallback_weight = 1.0, HashMap<NodePath,float>* p_weights = NULL); void _process_animation(float p_delta); bool reset_request; @@ -283,6 +284,8 @@ private: void _recompute_caches(); void _recompute_caches(const StringName& p_node); DVector<String> _get_node_list(); + + void _compute_weights(float *p_fallback_weight, HashMap<NodePath,float> *p_weights, float p_coeff, const HashMap<NodePath,bool> *p_filter = NULL, float p_filtered_coeff = 0); protected: diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 31cef85aa0..b4fa463cde 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -662,8 +662,7 @@ void RichTextLabel::_notification(int p_what) { } break; case NOTIFICATION_ENTER_TREE: { - if (use_bbcode) - parse_bbcode(bbcode); + set_bbcode(bbcode); main->first_invalid_line=0; //invalidate ALL update(); @@ -1440,7 +1439,6 @@ bool RichTextLabel::is_scroll_following() const { Error RichTextLabel::parse_bbcode(const String& p_bbcode) { - clear(); return append_bbcode(p_bbcode); } @@ -1858,6 +1856,10 @@ void RichTextLabel::set_bbcode(const String& p_bbcode) { bbcode=p_bbcode; if (is_inside_tree() && use_bbcode) parse_bbcode(p_bbcode); + else { // raw text + clear(); + add_text(p_bbcode); + } } String RichTextLabel::get_bbcode() const { @@ -1869,19 +1871,37 @@ void RichTextLabel::set_use_bbcode(bool p_enable) { if (use_bbcode==p_enable) return; use_bbcode=p_enable; - if (is_inside_tree() && use_bbcode) - parse_bbcode(bbcode); + set_bbcode(bbcode); } bool RichTextLabel::is_using_bbcode() const { return use_bbcode; } + +String RichTextLabel::get_text() { + String text = ""; + Item *it = main; + while (it) { + if (it->type == ITEM_TEXT) { + ItemText *t = static_cast<ItemText*>(it); + text += t->text; + } else if (it->type == ITEM_NEWLINE) { + text += "\n"; + } else if (it->type == ITEM_INDENT) { + text += "\t"; + } + it=_get_next_item(it,true); + } + return text; +} + void RichTextLabel::_bind_methods() { ObjectTypeDB::bind_method(_MD("_input_event"),&RichTextLabel::_input_event); ObjectTypeDB::bind_method(_MD("_scroll_changed"),&RichTextLabel::_scroll_changed); + ObjectTypeDB::bind_method(_MD("get_text"),&RichTextLabel::get_text); ObjectTypeDB::bind_method(_MD("add_text","text"),&RichTextLabel::add_text); ObjectTypeDB::bind_method(_MD("add_image","image:Texture"),&RichTextLabel::add_image); ObjectTypeDB::bind_method(_MD("newline"),&RichTextLabel::add_newline); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 635fe87ad4..5147905a0e 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -280,6 +280,7 @@ protected: public: + String get_text(); void add_text(const String& p_text); void add_image(const Ref<Texture>& p_image); void add_newline(); diff --git a/scene/resources/sample_library.cpp b/scene/resources/sample_library.cpp index 73517b180e..67481f267d 100644 --- a/scene/resources/sample_library.cpp +++ b/scene/resources/sample_library.cpp @@ -106,9 +106,9 @@ void SampleLibrary::remove_sample(const StringName& p_name) { sample_map.erase(p_name); } -void SampleLibrary::get_sample_list(List<StringName> *p_samples) { +void SampleLibrary::get_sample_list(List<StringName> *p_samples) const { - for(Map<StringName,SampleData >::Element *E=sample_map.front();E;E=E->next()) { + for(const Map<StringName,SampleData >::Element *E=sample_map.front();E;E=E->next()) { p_samples->push_back(E->key()); } @@ -177,7 +177,20 @@ float SampleLibrary::sample_get_pitch_scale(const StringName& p_name) const{ return sample_map[p_name].pitch_scale; } +Array SampleLibrary::_get_sample_list() const { + List<StringName> snames; + get_sample_list(&snames); + + snames.sort_custom<StringName::AlphCompare>(); + + Array ret; + for (List<StringName>::Element *E=snames.front();E;E=E->next()) { + ret.push_back(E->get()); + } + + return ret; +} void SampleLibrary::_bind_methods() { @@ -186,6 +199,8 @@ void SampleLibrary::_bind_methods() { ObjectTypeDB::bind_method(_MD("has_sample","name"),&SampleLibrary::has_sample ); ObjectTypeDB::bind_method(_MD("remove_sample","name"),&SampleLibrary::remove_sample ); + ObjectTypeDB::bind_method(_MD("get_sample_list"),&SampleLibrary::_get_sample_list ); + ObjectTypeDB::bind_method(_MD("sample_set_volume_db","name","db"),&SampleLibrary::sample_set_volume_db ); ObjectTypeDB::bind_method(_MD("sample_get_volume_db","name"),&SampleLibrary::sample_get_volume_db ); diff --git a/scene/resources/sample_library.h b/scene/resources/sample_library.h index 8377967106..e572aa215a 100644 --- a/scene/resources/sample_library.h +++ b/scene/resources/sample_library.h @@ -47,6 +47,8 @@ class SampleLibrary : public Resource { }; Map<StringName,SampleData > sample_map; + + Array _get_sample_list() const; protected: bool _set(const StringName& p_name, const Variant& p_value); @@ -66,7 +68,7 @@ public: void sample_set_pitch_scale(const StringName& p_name, float p_pitch); float sample_get_pitch_scale(const StringName& p_name) const; Ref<Sample> get_sample(const StringName& p_name) const; - void get_sample_list(List<StringName> *p_samples); + void get_sample_list(List<StringName> *p_samples) const; void remove_sample(const StringName& p_name); StringName get_sample_idx(int p_idx) const; diff --git a/servers/visual/visual_server_raster.cpp b/servers/visual/visual_server_raster.cpp index 526179c41f..d89ea887fa 100644 --- a/servers/visual/visual_server_raster.cpp +++ b/servers/visual/visual_server_raster.cpp @@ -4448,12 +4448,13 @@ void VisualServerRaster::cursor_set_rotation(float p_rotation, int p_cursor) { cursors[p_cursor].rot = p_rotation; }; -void VisualServerRaster::cursor_set_texture(RID p_texture, const Point2 &p_center_offset, int p_cursor) { +void VisualServerRaster::cursor_set_texture(RID p_texture, const Point2 &p_center_offset, int p_cursor, const Rect2 &p_region) { VS_CHANGED; ERR_FAIL_INDEX(p_cursor, MAX_CURSORS); cursors[p_cursor].texture = p_texture; cursors[p_cursor].center = p_center_offset; + cursors[p_cursor].region = p_region; }; void VisualServerRaster::cursor_set_visible(bool p_visible, int p_cursor) { @@ -7530,8 +7531,13 @@ void VisualServerRaster::_draw_cursors_and_margins() { RID tex = cursors[i].texture?cursors[i].texture:default_cursor_texture; ERR_CONTINUE( !tex ); - Point2 size(texture_get_width(tex), texture_get_height(tex)); - rasterizer->canvas_draw_rect(Rect2(cursors[i].pos, size), 0, Rect2(), tex, Color(1, 1, 1, 1)); + if (cursors[i].region.has_no_area()) { + Point2 size(texture_get_width(tex), texture_get_height(tex)); + rasterizer->canvas_draw_rect(Rect2(cursors[i].pos, size), 0, Rect2(), tex, Color(1, 1, 1, 1)); + } else { + Point2 size = cursors[i].region.size; + rasterizer->canvas_draw_rect(Rect2(cursors[i].pos, size), Rasterizer::CANVAS_RECT_REGION, cursors[i].region, tex, Color(1, 1, 1, 1)); + } }; diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h index 0480d9f5cb..228a4a7c44 100644 --- a/servers/visual/visual_server_raster.h +++ b/servers/visual/visual_server_raster.h @@ -544,10 +544,12 @@ class VisualServerRaster : public VisualServer { RID texture; Point2 center; bool visible; + Rect2 region; Cursor() { rot = 0; visible = false; + region = Rect2(); }; }; @@ -1240,7 +1242,7 @@ public: /* CURSOR */ virtual void cursor_set_rotation(float p_rotation, int p_cursor = 0); // radians - virtual void cursor_set_texture(RID p_texture, const Point2 &p_center_offset, int p_cursor=0); + virtual void cursor_set_texture(RID p_texture, const Point2 &p_center_offset, int p_cursor=0, const Rect2 &p_region=Rect2()); virtual void cursor_set_visible(bool p_visible, int p_cursor = 0); virtual void cursor_set_pos(const Point2& p_pos, int p_cursor = 0); diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h index a97b232c03..8c39b0bea1 100644 --- a/servers/visual/visual_server_wrap_mt.h +++ b/servers/visual/visual_server_wrap_mt.h @@ -680,7 +680,7 @@ public: /* CURSOR */ FUNC2(cursor_set_rotation,float , int ); // radians - FUNC3(cursor_set_texture,RID , const Point2 &, int ); + FUNC4(cursor_set_texture,RID , const Point2 &, int, const Rect2 &); FUNC2(cursor_set_visible,bool , int ); FUNC2(cursor_set_pos,const Point2& , int ); diff --git a/servers/visual_server.h b/servers/visual_server.h index f330a6faee..64318dfd72 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -1110,7 +1110,7 @@ public: /* CURSOR */ virtual void cursor_set_rotation(float p_rotation, int p_cursor = 0)=0; // radians - virtual void cursor_set_texture(RID p_texture, const Point2 &p_center_offset = Point2(0, 0), int p_cursor=0)=0; + virtual void cursor_set_texture(RID p_texture, const Point2 &p_center_offset = Point2(0, 0), int p_cursor=0, const Rect2 &p_region=Rect2())=0; virtual void cursor_set_visible(bool p_visible, int p_cursor = 0)=0; virtual void cursor_set_pos(const Point2& p_pos, int p_cursor = 0)=0; diff --git a/tools/editor/animation_editor.cpp b/tools/editor/animation_editor.cpp index c7b78b980f..9eb6b4013f 100644 --- a/tools/editor/animation_editor.cpp +++ b/tools/editor/animation_editor.cpp @@ -693,11 +693,103 @@ void AnimationKeyEditor::_menu_add_track(int p_type) { } } +void AnimationKeyEditor::_anim_duplicate_keys(bool transpose) { + //duplicait! + if (selection.size() && animation.is_valid() && selected_track>=0 && selected_track<animation->get_track_count()) { + + int top_track=0x7FFFFFFF; + float top_time = 1e10; + for(Map<SelectedKey,KeyInfo>::Element *E=selection.back();E;E=E->prev()) { + + const SelectedKey &sk = E->key(); + + float t = animation->track_get_key_time(sk.track,sk.key); + if (t<top_time) + top_time=t; + if (sk.track<top_track) + top_track=sk.track; + + } + ERR_FAIL_COND( top_track == 0x7FFFFFFF || top_time==1e10 ); + + // + + int start_track = transpose ? selected_track : top_track; + + undo_redo->create_action(TTR("Anim Duplicate Keys")); + + List<Pair<int,float> > new_selection_values; + + for(Map<SelectedKey,KeyInfo>::Element *E=selection.back();E;E=E->prev()) { + + const SelectedKey &sk = E->key(); + + float t = animation->track_get_key_time(sk.track,sk.key); + + float dst_time = t+(timeline_pos - top_time); + int dst_track = sk.track + (start_track - top_track); + + if (dst_track < 0 || dst_track>= animation->get_track_count()) + continue; + + if (animation->track_get_type(dst_track) != animation->track_get_type(sk.track)) + continue; + + int existing_idx = animation->track_find_key(dst_track,dst_time,true); + + undo_redo->add_do_method(animation.ptr(),"track_insert_key",dst_track,dst_time,animation->track_get_key_value(E->key().track,E->key().key),animation->track_get_key_transition(E->key().track,E->key().key)); + undo_redo->add_undo_method(animation.ptr(),"track_remove_key_at_pos",dst_track,dst_time); + + Pair<int,float> p; + p.first=dst_track; + p.second=dst_time; + new_selection_values.push_back( p ); + + if (existing_idx!=-1) { + + undo_redo->add_undo_method(animation.ptr(),"track_insert_key",dst_track,dst_time,animation->track_get_key_value(dst_track,existing_idx),animation->track_get_key_transition(dst_track,existing_idx)); + + } + + } + + undo_redo->commit_action(); + + //reselect duplicated + + Map<SelectedKey,KeyInfo> new_selection; + for (List<Pair<int,float> >::Element *E=new_selection_values.front();E;E=E->next()) { + + int track=E->get().first; + float time = E->get().second; + + int existing_idx = animation->track_find_key(track,time,true); + + if (existing_idx==-1) + continue; + SelectedKey sk2; + sk2.track=track; + sk2.key=existing_idx; + + KeyInfo ki; + ki.pos=time; + + new_selection[sk2]=ki; + + } + + + selection=new_selection; + track_editor->update(); + _edit_if_single_selection(); + + } +} + void AnimationKeyEditor::_menu_track(int p_type) { ERR_FAIL_COND(!animation.is_valid()); - last_menu_track_opt=p_type; switch(p_type) { @@ -765,108 +857,7 @@ void AnimationKeyEditor::_menu_track(int p_type) { case TRACK_MENU_DUPLICATE: case TRACK_MENU_DUPLICATE_TRANSPOSE: { - - //duplicait! - if (selection.size() && animation.is_valid() && selected_track>=0 && selected_track<animation->get_track_count()) { - - - int top_track=0x7FFFFFFF; - float top_time = 1e10; - for(Map<SelectedKey,KeyInfo>::Element *E=selection.back();E;E=E->prev()) { - - const SelectedKey &sk = E->key(); - - float t = animation->track_get_key_time(sk.track,sk.key); - if (t<top_time) - top_time=t; - if (sk.track<top_track) - top_track=sk.track; - - - } - ERR_FAIL_COND( top_track == 0x7FFFFFFF || top_time==1e10 ); - - // - - int start_track = p_type==TRACK_MENU_DUPLICATE_TRANSPOSE ? selected_track : top_track; - - - undo_redo->create_action(TTR("Anim Duplicate Keys")); - - List<Pair<int,float> > new_selection_values; - - for(Map<SelectedKey,KeyInfo>::Element *E=selection.back();E;E=E->prev()) { - - const SelectedKey &sk = E->key(); - - float t = animation->track_get_key_time(sk.track,sk.key); - - float dst_time = t+(timeline_pos - top_time); - int dst_track = sk.track + (start_track - top_track); - - if (dst_track < 0 || dst_track>= animation->get_track_count()) - continue; - - if (animation->track_get_type(dst_track) != animation->track_get_type(sk.track)) - continue; - - int existing_idx = animation->track_find_key(dst_track,dst_time,true); - - undo_redo->add_do_method(animation.ptr(),"track_insert_key",dst_track,dst_time,animation->track_get_key_value(E->key().track,E->key().key),animation->track_get_key_transition(E->key().track,E->key().key)); - undo_redo->add_undo_method(animation.ptr(),"track_remove_key_at_pos",dst_track,dst_time); - - Pair<int,float> p; - p.first=dst_track; - p.second=dst_time; - new_selection_values.push_back( p ); - - if (existing_idx!=-1) { - - undo_redo->add_undo_method(animation.ptr(),"track_insert_key",dst_track,dst_time,animation->track_get_key_value(dst_track,existing_idx),animation->track_get_key_transition(dst_track,existing_idx)); - - } - - - - } - - undo_redo->commit_action(); - - //reselect duplicated - - Map<SelectedKey,KeyInfo> new_selection; - for (List<Pair<int,float> >::Element *E=new_selection_values.front();E;E=E->next()) { - - - int track=E->get().first; - float time = E->get().second; - - int existing_idx = animation->track_find_key(track,time,true); - - if (existing_idx==-1) - continue; - SelectedKey sk2; - sk2.track=track; - sk2.key=existing_idx; - - KeyInfo ki; - ki.pos=time; - - - new_selection[sk2]=ki; - - - } - - - selection=new_selection; - track_editor->update(); - _edit_if_single_selection(); - - - } - - + _anim_duplicate_keys(p_type==TRACK_MENU_DUPLICATE_TRANSPOSE); } break; case TRACK_MENU_SET_ALL_TRANS_LINEAR: case TRACK_MENU_SET_ALL_TRANS_CONSTANT: @@ -1618,9 +1609,7 @@ void AnimationKeyEditor::_track_menu_selected(int p_idx) { undo_redo->add_do_method(animation.ptr(),"track_set_interpolation_type",interp_editing,p_idx); undo_redo->add_undo_method(animation.ptr(),"track_set_interpolation_type",interp_editing,animation->track_get_interpolation_type(interp_editing)); undo_redo->commit_action(); - } - - if (cont_editing!=-1) { + } else if (cont_editing!=-1) { ERR_FAIL_INDEX(cont_editing,animation->get_track_count()); @@ -1628,6 +1617,16 @@ void AnimationKeyEditor::_track_menu_selected(int p_idx) { undo_redo->add_do_method(animation.ptr(),"value_track_set_update_mode",cont_editing,p_idx); undo_redo->add_undo_method(animation.ptr(),"value_track_set_update_mode",cont_editing,animation->value_track_get_update_mode(cont_editing)); undo_redo->commit_action(); + } else { + switch (p_idx) { + + case RIGHT_MENU_DUPLICATE: + _anim_duplicate_keys(); break; + case RIGHT_MENU_DUPLICATE_TRANSPOSE: + _anim_duplicate_keys(true); break; + case RIGHT_MENU_REMOVE: + _anim_delete_keys(); break; + } } } @@ -1789,6 +1788,25 @@ bool AnimationKeyEditor::_edit_if_single_selection() { } +void AnimationKeyEditor::_anim_delete_keys() { + if (selection.size()) { + undo_redo->create_action(TTR("Anim Delete Keys")); + + for(Map<SelectedKey,KeyInfo>::Element *E=selection.back();E;E=E->prev()) { + + undo_redo->add_do_method(animation.ptr(),"track_remove_key",E->key().track,E->key().key); + undo_redo->add_undo_method(animation.ptr(),"track_insert_key",E->key().track,E->get().pos,animation->track_get_key_value(E->key().track,E->key().key),animation->track_get_key_transition(E->key().track,E->key().key)); + + } + undo_redo->add_do_method(this,"_clear_selection_for_anim",animation); + undo_redo->add_undo_method(this,"_clear_selection_for_anim",animation); + undo_redo->commit_action(); + //selection.clear(); + accept_event(); + _edit_if_single_selection(); + } +} + void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) { Control *te=track_editor; @@ -1859,22 +1877,7 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) { } else if (p_input.key.scancode==KEY_DELETE && p_input.key.pressed && click.click==ClickOver::CLICK_NONE) { - if (selection.size()) { - undo_redo->create_action(TTR("Anim Delete Keys")); - - for(Map<SelectedKey,KeyInfo>::Element *E=selection.back();E;E=E->prev()) { - - undo_redo->add_do_method(animation.ptr(),"track_remove_key",E->key().track,E->key().key); - undo_redo->add_undo_method(animation.ptr(),"track_insert_key",E->key().track,E->get().pos,animation->track_get_key_value(E->key().track,E->key().key),animation->track_get_key_transition(E->key().track,E->key().key)); - - } - undo_redo->add_do_method(this,"_clear_selection_for_anim",animation); - undo_redo->add_undo_method(this,"_clear_selection_for_anim",animation); - undo_redo->commit_action(); - //selection.clear(); - accept_event(); - _edit_if_single_selection(); - } + _anim_delete_keys(); } else if (animation.is_valid() && animation->get_track_count()>0) { if (p_input.is_pressed() && (p_input.is_action("ui_up") || p_input.is_action("ui_page_up"))) { @@ -1934,6 +1937,116 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) { v_scroll->set_val( v_scroll->get_val() + v_scroll->get_page() / 8 ); } + if (mb.button_index==BUTTON_RIGHT && mb.pressed) { + + Point2 mpos = Point2(mb.x,mb.y)-ofs; + + if (selection.size() == 0) { + // Auto-select on right-click if nothing is selected + // Note: This code is pretty much duplicated from the left click code, + // both codes could be moved into a function to avoid the duplicated code. + Point2 mpos = Point2(mb.x,mb.y)-ofs; + + if (mpos.y < h ) { + return; + } + + mpos.y -= h; + + int idx = mpos.y / h; + idx+=v_scroll->get_val(); + if (idx <0 || idx>=animation->get_track_count()) + break; + + if (mpos.x < name_limit) { + } else if (mpos.x < settings_limit) { + float pos = mpos.x - name_limit; + pos/=_get_zoom_scale(); + pos+=h_scroll->get_val(); + float w_time = (type_icon[0]->get_width() / _get_zoom_scale())/2.0; + + int kidx = animation->track_find_key(idx,pos); + int kidx_n = kidx+1; + int key=-1; + + if (kidx>=0 && kidx<animation->track_get_key_count(idx)) { + + float kpos = animation->track_get_key_time(idx,kidx); + if (ABS(pos-kpos)<=w_time) { + + key=kidx; + } + } + + if (key==-1 && kidx_n>=0 && kidx_n<animation->track_get_key_count(idx)) { + + float kpos = animation->track_get_key_time(idx,kidx_n); + if (ABS(pos-kpos)<=w_time) { + + key=kidx_n; + } + } + + if (key==-1) { + + click.click=ClickOver::CLICK_SELECT_KEYS; + click.at=Point2(mb.x,mb.y); + click.to=click.at; + click.shift=mb.mod.shift; + selected_track=idx; + track_editor->update(); + //drag select region + return; + + } + + + + SelectedKey sk; + sk.track=idx; + sk.key=key; + KeyInfo ki; + ki.pos= animation->track_get_key_time(idx,key); + click.shift=mb.mod.shift; + click.selk=sk; + + + if (!mb.mod.shift && !selection.has(sk)) + _clear_selection(); + + selection.insert(sk,ki); + + click.click=ClickOver::CLICK_MOVE_KEYS; + click.at=Point2(mb.x,mb.y); + click.to=click.at; + update(); + selected_track=idx; + track_editor->update(); + + if (_edit_if_single_selection() && mb.mod.command) { + edit_button->set_pressed(true); + key_editor_tab->show(); + } + } + } + + if (selection.size()) { + // User has right clicked and we have a selection, show a popup menu with options + track_menu->clear(); + track_menu->set_size(Point2(1,1)); + track_menu->add_item(TTR("Duplicate Selection"), RIGHT_MENU_DUPLICATE); + track_menu->add_item(TTR("Duplicate Transposed"), RIGHT_MENU_DUPLICATE_TRANSPOSE); + track_menu->add_item(TTR("Remove Selection"), RIGHT_MENU_REMOVE); + + track_menu->set_pos(te->get_global_pos()+mpos); + + interp_editing=-1; + cont_editing=-1; + + track_menu->popup(); + } + } + if (mb.button_index==BUTTON_LEFT && !(mb.button_mask&~BUTTON_MASK_LEFT)) { diff --git a/tools/editor/animation_editor.h b/tools/editor/animation_editor.h index 413c73b4b9..131100a205 100644 --- a/tools/editor/animation_editor.h +++ b/tools/editor/animation_editor.h @@ -99,6 +99,12 @@ class AnimationKeyEditor : public VBoxContainer { CURVE_SET_CONSTANT }; + enum { + RIGHT_MENU_DUPLICATE, + RIGHT_MENU_DUPLICATE_TRANSPOSE, + RIGHT_MENU_REMOVE + }; + struct MouseOver { enum Over { @@ -313,6 +319,9 @@ class AnimationKeyEditor : public VBoxContainer { void _add_call_track(const NodePath& p_base); + void _anim_duplicate_keys(bool transpose = false); + void _anim_delete_keys(); + void _root_removed(); protected: diff --git a/tools/editor/create_dialog.cpp b/tools/editor/create_dialog.cpp index 5275e1beeb..210b799f3d 100644 --- a/tools/editor/create_dialog.cpp +++ b/tools/editor/create_dialog.cpp @@ -42,12 +42,13 @@ void CreateDialog::popup(bool p_dontclear) { - popup_centered_ratio(0.6); + popup_centered_ratio(); if (p_dontclear) search_box->select_all(); else search_box->clear(); search_box->grab_focus(); + _update_search(); @@ -165,9 +166,10 @@ void CreateDialog::_update_search() { if (!ObjectTypeDB::can_instance(type)) continue; // cant create what can't be instanced - if (search_box->get_text()=="") + + if (search_box->get_text()=="") { add_type(type,types,root,&to_select); - else { + } else { bool found=false; String type=I->get(); @@ -186,7 +188,7 @@ void CreateDialog::_update_search() { add_type(I->get(),types,root,&to_select); } - if (EditorNode::get_editor_data().get_custom_types().has(type)) { + if (EditorNode::get_editor_data().get_custom_types().has(type) && ObjectTypeDB::is_type(type, base_type)) { //there are custom types based on this... cool. //print_line("there are custom types"); @@ -198,6 +200,7 @@ void CreateDialog::_update_search() { if (!show) continue; + if (!types.has(type)) add_type(type,types,root,&to_select); @@ -216,7 +219,7 @@ void CreateDialog::_update_search() { } - if (!to_select && (search_box->get_text()=="" || ct[i].name.findn(search_box->get_text())!=-1)) { + if (!to_select) { to_select=item; } @@ -246,7 +249,6 @@ void CreateDialog::_notification(int p_what) { if (p_what==NOTIFICATION_ENTER_TREE) { connect("confirmed",this,"_confirmed"); - _update_search(); } if (p_what==NOTIFICATION_EXIT_TREE) { diff --git a/tools/editor/editor_autoload_settings.cpp b/tools/editor/editor_autoload_settings.cpp new file mode 100644 index 0000000000..1cf0090e7e --- /dev/null +++ b/tools/editor/editor_autoload_settings.cpp @@ -0,0 +1,618 @@ +/*************************************************************************/ +/* editor_autoload_settings.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "editor_autoload_settings.h" + +#include "globals.h" +#include "global_constants.h" + +#include "editor_node.h" + +#define PREVIEW_LIST_MAX_SIZE 10 + +StringName EditorAutoloadSettings::autoload_changed = StringName(); + +void EditorAutoloadSettings::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE) { + + List<String> afn; + ResourceLoader::get_recognized_extensions_for_type("Script", &afn); + ResourceLoader::get_recognized_extensions_for_type("PackedScene", &afn); + + EditorFileDialog *file_dialog = autoload_add_path->get_file_dialog(); + + for (List<String>::Element *E = afn.front(); E; E = E->next()) { + + file_dialog->add_filter("*." + E->get()); + } + } +} + +bool EditorAutoloadSettings::_autoload_name_is_valid(const String& p_name, String* r_error) { + + if (!p_name.is_valid_identifier()) { + if (r_error) + *r_error = TTR("Invalid name.") + "\n" + TTR("Valid characters:")+" a-z, A-Z, 0-9 or _"; + + return false; + } + + if (ObjectTypeDB::type_exists(p_name)) { + if (r_error) + *r_error = TTR("Invalid name. Must not collide with an existing engine class name."); + + return false; + } + + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + if (Variant::get_type_name( Variant::Type(i) ) == p_name) { + if (r_error) + *r_error = TTR("Invalid name. Must not collide with an existing buit-in type name."); + + return false; + } + } + + for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) { + if (GlobalConstants::get_global_constant_name(i) == p_name) { + if (r_error) + *r_error = TTR("Invalid name. Must not collide with an existing global constant name."); + + return false; + } + } + + return true; +} + +void EditorAutoloadSettings::_autoload_add() { + + String name = autoload_add_name->get_text(); + + String error; + if (!_autoload_name_is_valid(name, &error)) { + EditorNode::get_singleton()->show_warning(error); + return; + } + + String path = autoload_add_path->get_line_edit()->get_text(); + if (!FileAccess::exists(path)) { + EditorNode::get_singleton()->show_warning(TTR("Invalid Path.") + "\n" + TTR("File does not exist.")); + return; + } + + if (!path.begins_with("res://")) { + EditorNode::get_singleton()->show_warning(TTR("Invalid Path.") + "\n"+ TTR("Not in resource path.")); + return; + } + + name = "autoload/" + name; + + UndoRedo* undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + undo_redo->create_action(TTR("Add AutoLoad")); + undo_redo->add_do_property(Globals::get_singleton(), name, "*" + path); + undo_redo->add_do_method(Globals::get_singleton(), "set_persisting", name, true); + + if (Globals::get_singleton()->has(name)) { + undo_redo->add_undo_property(Globals::get_singleton(), name, Globals::get_singleton()->get(name)); + } else { + undo_redo->add_undo_property(Globals::get_singleton(), name, Variant()); + } + + undo_redo->add_do_method(this, "update_autoload"); + undo_redo->add_undo_method(this, "update_autoload"); + + undo_redo->add_do_method(this, "emit_signal", autoload_changed); + undo_redo->add_undo_method(this, "emit_signal", autoload_changed); + + undo_redo->commit_action(); + + autoload_add_path->get_line_edit()->set_text(""); + autoload_add_name->set_text(""); +} + +void EditorAutoloadSettings::_autoload_selected() { + + TreeItem *ti = tree->get_selected(); + + if (!ti) + return; + + selected_autoload = "autoload/" + ti->get_text(0); +} + +void EditorAutoloadSettings::_autoload_edited() { + + if (updating_autoload) + return; + + TreeItem *ti = tree->get_edited(); + int column = tree->get_edited_column(); + + UndoRedo *undo_redo = EditorNode::get_undo_redo(); + + if (column == 0) { + String name = ti->get_text(0); + String old_name = selected_autoload.get_slice("/", 1); + + if (name == old_name) + return; + + String error; + if (!_autoload_name_is_valid(name, &error)) { + ti->set_text(0, old_name); + EditorNode::get_singleton()->show_warning(error); + return; + } + + if (Globals::get_singleton()->has("autoload/" + name)) { + ti->set_text(0, old_name); + EditorNode::get_singleton()->show_warning(vformat(TTR("Autoload '%s' already exists!"), name)); + return; + } + + updating_autoload = true; + + name = "autoload/" + name; + + bool persisting = Globals::get_singleton()->get(selected_autoload); + int order = Globals::get_singleton()->get(selected_autoload); + String path = Globals::get_singleton()->get(selected_autoload); + + undo_redo->create_action(TTR("Rename Autoload")); + + undo_redo->add_do_property(Globals::get_singleton(), name, path); + undo_redo->add_do_method(Globals::get_singleton(), "set_persisting", name, persisting); + undo_redo->add_do_method(Globals::get_singleton(), "set_order", name, order); + undo_redo->add_do_method(Globals::get_singleton(), "clear", selected_autoload); + + undo_redo->add_undo_property(Globals::get_singleton(), selected_autoload, path); + undo_redo->add_undo_method(Globals::get_singleton(), "set_persisting", selected_autoload, persisting); + undo_redo->add_undo_method(Globals::get_singleton(), "set_order", selected_autoload, order); + undo_redo->add_undo_method(Globals::get_singleton(), "clear", name); + + undo_redo->add_do_method(this, "update_autoload"); + undo_redo->add_undo_method(this, "update_autoload"); + + undo_redo->add_do_method(this, "emit_signal", autoload_changed); + undo_redo->add_undo_method(this, "emit_signal", autoload_changed); + + undo_redo->commit_action(); + + selected_autoload = name; + } else if (column == 2) { + updating_autoload = true; + + bool checked = ti->is_checked(2); + String base = "autoload/" + ti->get_text(0); + + int order = Globals::get_singleton()->get_order(base); + String path = Globals::get_singleton()->get(base); + + if (path.begins_with("*")) + path = path.substr(1, path.length()); + + if (checked) + path = "*" + path; + + undo_redo->create_action(TTR("Toggle AutoLoad Globals")); + + undo_redo->add_do_property(Globals::get_singleton(), base, path); + undo_redo->add_undo_property(Globals::get_singleton(), base, Globals::get_singleton()->get(base)); + + undo_redo->add_do_method(Globals::get_singleton(),"set_order", base, order); + undo_redo->add_undo_method(Globals::get_singleton(),"set_order", base, order); + + undo_redo->add_do_method(this, "update_autoload"); + undo_redo->add_undo_method(this, "update_autoload"); + + undo_redo->add_do_method(this, "emit_signal", autoload_changed); + undo_redo->add_undo_method(this, "emit_signal", autoload_changed); + + undo_redo->commit_action(); + } + + updating_autoload = false; +} + +void EditorAutoloadSettings::_autoload_button_pressed(Object *p_item, int p_column, int p_button) { + + TreeItem *ti = p_item->cast_to<TreeItem>(); + + String name = "autoload/" + ti->get_text(0); + + UndoRedo *undo_redo = EditorNode::get_undo_redo(); + + switch (p_button) { + + case BUTTON_MOVE_UP: + case BUTTON_MOVE_DOWN: { + + TreeItem *swap = NULL; + + if (p_button == BUTTON_MOVE_UP) { + swap = ti->get_prev(); + } else { + swap = ti->get_next(); + } + + if (!swap) + return; + + String swap_name = "autoload/" + swap->get_text(0); + + int order = Globals::get_singleton()->get_order(name); + int swap_order = Globals::get_singleton()->get_order(swap_name); + + undo_redo->create_action(TTR("Move Autoload")); + + undo_redo->add_do_method(Globals::get_singleton(), "set_order", name, swap_order); + undo_redo->add_undo_method(Globals::get_singleton(), "set_order", name, order); + + undo_redo->add_do_method(Globals::get_singleton(), "set_order", swap_name, order); + undo_redo->add_undo_method(Globals::get_singleton(), "set_order", swap_name, swap_order); + + undo_redo->add_do_method(this, "update_autoload"); + undo_redo->add_undo_method(this, "update_autoload"); + + undo_redo->add_do_method(this, "emit_signal", autoload_changed); + undo_redo->add_undo_method(this, "emit_signal", autoload_changed); + + undo_redo->commit_action(); + } break; + case BUTTON_DELETE: { + + int order = Globals::get_singleton()->get_order(name); + + undo_redo->create_action(TTR("Remove Autoload")); + + undo_redo->add_do_property(Globals::get_singleton(), name, Variant()); + + undo_redo->add_undo_property(Globals::get_singleton(), name, Globals::get_singleton()->get(name)); + undo_redo->add_undo_method(Globals::get_singleton(), "set_persisting", name, true); + undo_redo->add_undo_method(Globals::get_singleton(), "set_order", order); + + undo_redo->add_do_method(this, "update_autoload"); + undo_redo->add_undo_method(this, "update_autoload"); + + undo_redo->add_do_method(this, "emit_signal", autoload_changed); + undo_redo->add_undo_method(this, "emit_signal", autoload_changed); + + undo_redo->commit_action(); + } break; + } +} + +void EditorAutoloadSettings::_autoload_file_callback(const String& p_path) { + + autoload_add_name->set_text(p_path.get_file().basename()); +} + +void EditorAutoloadSettings::update_autoload() { + + if (updating_autoload) + return; + + updating_autoload = true; + + autoload_cache.clear(); + + tree->clear(); + TreeItem *root = tree->create_item(); + + List<PropertyInfo> props; + Globals::get_singleton()->get_property_list(&props); + + for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { + + const PropertyInfo &pi = E->get(); + + if (!pi.name.begins_with("autoload/")) + continue; + + String name = pi.name.get_slice("/", 1); + String path = Globals::get_singleton()->get(pi.name); + + if (name.empty()) + continue; + + AutoLoadInfo info; + info.name = pi.name; + info.order = Globals::get_singleton()->get_order(pi.name); + + autoload_cache.push_back(info); + + bool global = false; + + if (path.begins_with("*")) { + global = true; + path = path.substr(1, path.length()); + } + + TreeItem *item = tree->create_item(root); + item->set_text(0, name); + item->set_editable(0, true); + + item->set_text(1, path); + item->set_selectable(1, false); + + item->set_cell_mode(2, TreeItem::CELL_MODE_CHECK); + item->set_editable(2, true); + item->set_text(2, TTR("Enable")); + item->set_checked(2, global); + + item->add_button(3, get_icon("MoveUp","EditorIcons"), BUTTON_MOVE_UP); + item->add_button(3, get_icon("MoveDown","EditorIcons"), BUTTON_MOVE_DOWN); + item->add_button(3, get_icon("Del","EditorIcons"), BUTTON_DELETE); + item->set_selectable(3, false); + } + + updating_autoload = false; +} + +Variant EditorAutoloadSettings::get_drag_data_fw(const Point2& p_point, Control *p_control) { + + if (autoload_cache.size() <= 1) + return false; + + StringArray autoloads; + + TreeItem *next = tree->get_next_selected(NULL); + + while (next) { + autoloads.push_back(next->get_text(0)); + next = tree->get_next_selected(next); + } + + if (autoloads.size() == 0 || autoloads.size() == autoload_cache.size()) + return Variant(); + + VBoxContainer *preview = memnew( VBoxContainer ); + + int max_size = MIN(PREVIEW_LIST_MAX_SIZE, autoloads.size()); + + for (int i = 0; i < max_size; i++) { + Label *label = memnew( Label(autoloads[i]) ); + label->set_self_opacity(Math::lerp(1, 0, float(i)/PREVIEW_LIST_MAX_SIZE)); + + preview->add_child(label); + } + + tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN); + tree->set_drag_preview(preview); + + Dictionary drop_data; + drop_data["type"] = "autoload"; + drop_data["autoloads"] = autoloads; + + return drop_data; +} + +bool EditorAutoloadSettings::can_drop_data_fw(const Point2& p_point, const Variant& p_data, Control *p_control) const { + if (updating_autoload) + return false; + + Dictionary drop_data = p_data; + + if (!drop_data.has("type")) + return false; + + if (drop_data.has("type")) { + TreeItem *ti = tree->get_item_at_pos(p_point); + + if (!ti) + return false; + + int section = tree->get_drop_section_at_pos(p_point); + + if (section < -1) + return false; + + return true; + } + + return false; +} + +void EditorAutoloadSettings::drop_data_fw(const Point2& p_point, const Variant& p_data, Control *p_control) { + + TreeItem *ti = tree->get_item_at_pos(p_point); + + if (!ti) + return; + + int section = tree->get_drop_section_at_pos(p_point); + + if (section < -1) + return; + + String name; + bool move_to_back = false; + + if (section < 0) { + name = ti->get_text(0); + } else if (ti->get_next()) { + name = ti->get_next()->get_text(0); + } else { + name = ti->get_text(0); + move_to_back = true; + } + + int order = Globals::get_singleton()->get_order("autoload/" + name); + + AutoLoadInfo aux; + List<AutoLoadInfo>::Element *E = NULL; + + if (!move_to_back) { + aux.order = order; + E = autoload_cache.find(aux); + } + + Dictionary drop_data = p_data; + StringArray autoloads = drop_data["autoloads"]; + + Vector<int> orders; + orders.resize(autoload_cache.size()); + + for (int i = 0; i < autoloads.size(); i++) { + aux.order = Globals::get_singleton()->get_order("autoload/" + autoloads[i]); + + List<AutoLoadInfo>::Element *I = autoload_cache.find(aux); + + if (move_to_back) { + autoload_cache.move_to_back(I); + } else if (E != I) { + autoload_cache.move_before(I, E); + } else if (E->next()) { + E = E->next(); + } else { + break; + } + } + + int i = 0; + + for (List<AutoLoadInfo>::Element *E = autoload_cache.front(); E; E = E->next()) { + orders[i++] = E->get().order; + } + + orders.sort(); + + UndoRedo *undo_redo = EditorNode::get_undo_redo(); + + undo_redo->create_action(TTR("Rearrange Autoloads")); + + i = 0; + + for (List<AutoLoadInfo>::Element *E = autoload_cache.front(); E; E = E->next()) { + undo_redo->add_do_method(Globals::get_singleton(), "set_order", E->get().name, orders[i++]); + undo_redo->add_undo_method(Globals::get_singleton(), "set_order", E->get().name, E->get().order); + } + + orders.clear(); + + undo_redo->add_do_method(this, "update_autoload"); + undo_redo->add_undo_method(this, "update_autoload"); + + undo_redo->add_do_method(this, "emit_signal", autoload_changed); + undo_redo->add_undo_method(this, "emit_signal", autoload_changed); + + undo_redo->commit_action(); +} + +void EditorAutoloadSettings::_bind_methods() { + + ObjectTypeDB::bind_method("_autoload_add", &EditorAutoloadSettings::_autoload_add); + ObjectTypeDB::bind_method("_autoload_selected", &EditorAutoloadSettings::_autoload_selected); + ObjectTypeDB::bind_method("_autoload_edited", &EditorAutoloadSettings::_autoload_edited); + ObjectTypeDB::bind_method("_autoload_button_pressed", &EditorAutoloadSettings::_autoload_button_pressed); + ObjectTypeDB::bind_method("_autoload_file_callback", &EditorAutoloadSettings::_autoload_file_callback); + + ObjectTypeDB::bind_method("get_drag_data_fw", &EditorAutoloadSettings::get_drag_data_fw); + ObjectTypeDB::bind_method("can_drop_data_fw", &EditorAutoloadSettings::can_drop_data_fw); + ObjectTypeDB::bind_method("drop_data_fw", &EditorAutoloadSettings::drop_data_fw); + + ObjectTypeDB::bind_method("update_autoload", &EditorAutoloadSettings::update_autoload); + + ADD_SIGNAL(MethodInfo("autoload_changed")); +} + +EditorAutoloadSettings::EditorAutoloadSettings() { + + autoload_changed = "autoload_changed"; + + updating_autoload = false; + selected_autoload = ""; + + HBoxContainer *hbc = memnew( HBoxContainer ); + add_child(hbc); + + VBoxContainer *vbc_path = memnew( VBoxContainer ); + vbc_path->set_h_size_flags(SIZE_EXPAND_FILL); + + autoload_add_path = memnew( EditorLineEditFileChooser ); + autoload_add_path->set_h_size_flags(SIZE_EXPAND_FILL); + + autoload_add_path->get_file_dialog()->set_mode(EditorFileDialog::MODE_OPEN_FILE); + autoload_add_path->get_file_dialog()->connect("file_selected", this, "_autoload_file_callback"); + + vbc_path->add_margin_child(TTR("Path:"), autoload_add_path); + hbc->add_child(vbc_path); + + VBoxContainer *vbc_name = memnew( VBoxContainer ); + vbc_name->set_h_size_flags(SIZE_EXPAND_FILL); + + HBoxContainer *hbc_name = memnew( HBoxContainer ); + + autoload_add_name = memnew( LineEdit ); + autoload_add_name->set_h_size_flags(SIZE_EXPAND_FILL); + hbc_name->add_child(autoload_add_name); + + Button *add_autoload = memnew( Button ); + add_autoload->set_text(TTR("Add")); + hbc_name->add_child(add_autoload); + add_autoload->connect("pressed", this, "_autoload_add"); + + vbc_name->add_margin_child(TTR("Node Name:"), hbc_name); + hbc->add_child(vbc_name); + + tree = memnew( Tree ); + tree->set_hide_root(true); + tree->set_select_mode(Tree::SELECT_MULTI); + tree->set_single_select_cell_editing_only_when_already_selected(true); + + tree->set_drag_forwarding(this); + + tree->set_columns(4); + tree->set_column_titles_visible(true); + + tree->set_column_title(0,TTR("Name")); + tree->set_column_expand(0,true); + tree->set_column_min_width(0,100); + + tree->set_column_title(1,TTR("Path")); + tree->set_column_expand(1,true); + tree->set_column_min_width(1,100); + + tree->set_column_title(2,TTR("Singleton")); + tree->set_column_expand(2,false); + tree->set_column_min_width(2,80); + + tree->set_column_expand(3,false); + tree->set_column_min_width(3,80); + + tree->connect("cell_selected", this, "_autoload_selected"); + tree->connect("item_edited", this, "_autoload_edited"); + tree->connect("button_pressed", this, "_autoload_button_pressed"); + + add_margin_child(TTR("List:"), tree, true); +} + diff --git a/tools/editor/editor_autoload_settings.h b/tools/editor/editor_autoload_settings.h new file mode 100644 index 0000000000..b8825f807c --- /dev/null +++ b/tools/editor/editor_autoload_settings.h @@ -0,0 +1,94 @@ +/*************************************************************************/ +/* editor_autoload_settings.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef EDITOR_AUTOLOAD_SETTINGS_H +#define EDITOR_AUTOLOAD_SETTINGS_H + +#include "scene/gui/tree.h" + +#include "editor_file_dialog.h" + +class EditorAutoloadSettings : public VBoxContainer { + + OBJ_TYPE( EditorAutoloadSettings, VBoxContainer ); + + enum { + BUTTON_MOVE_UP, + BUTTON_MOVE_DOWN, + BUTTON_DELETE + }; + + static StringName autoload_changed; + + struct AutoLoadInfo { + String name; + int order; + + bool operator==(const AutoLoadInfo& p_info) { + return order == p_info.order; + } + }; + + List<AutoLoadInfo> autoload_cache; + + bool updating_autoload; + int number_of_autoloads; + String selected_autoload; + + Tree *tree; + EditorLineEditFileChooser *autoload_add_path; + LineEdit *autoload_add_name; + + bool _autoload_name_is_valid(const String& p_string, String *r_error = NULL); + + void _autoload_add(); + void _autoload_selected(); + void _autoload_edited(); + void _autoload_button_pressed(Object *p_item, int p_column, int p_button); + void _autoload_file_callback(const String& p_path); + + Variant get_drag_data_fw(const Point2& p_point, Control *p_from); + bool can_drop_data_fw(const Point2& p_point, const Variant& p_data, Control *p_from) const; + void drop_data_fw(const Point2& p_point, const Variant& p_data, Control *p_from); + +protected: + + void _notification(int p_what); + static void _bind_methods(); + +public: + + void update_autoload(); + + EditorAutoloadSettings(); + +}; + +#endif + diff --git a/tools/editor/editor_data.cpp b/tools/editor/editor_data.cpp index 0f10041034..8d3fd6c9c2 100644 --- a/tools/editor/editor_data.cpp +++ b/tools/editor/editor_data.cpp @@ -326,6 +326,13 @@ Dictionary EditorData::get_editor_states() const { } +Dictionary EditorData::get_scene_editor_states(int p_idx) const +{ + ERR_FAIL_INDEX_V(p_idx,edited_scene.size(),Dictionary()); + EditedScene es = edited_scene[p_idx]; + return es.editor_states; +} + void EditorData::set_editor_states(const Dictionary& p_states) { List<Variant> keys; @@ -613,11 +620,14 @@ void EditorData::set_edited_scene(int p_idx){ current_edited_scene=p_idx; //swap } -Node* EditorData::get_edited_scene_root(){ - - ERR_FAIL_INDEX_V(current_edited_scene,edited_scene.size(),NULL); - - return edited_scene[current_edited_scene].root; +Node* EditorData::get_edited_scene_root(int p_idx){ + if (p_idx < 0) { + ERR_FAIL_INDEX_V(current_edited_scene,edited_scene.size(),NULL); + return edited_scene[current_edited_scene].root; + } else { + ERR_FAIL_INDEX_V(p_idx,edited_scene.size(),NULL); + return edited_scene[p_idx].root; + } } void EditorData::set_edited_scene_root(Node* p_root) { @@ -630,9 +640,14 @@ int EditorData::get_edited_scene_count() const { return edited_scene.size(); } -void EditorData::set_edited_scene_version(uint64_t version) { +void EditorData::set_edited_scene_version(uint64_t version, int scene_idx) { ERR_FAIL_INDEX(current_edited_scene,edited_scene.size()); - edited_scene[current_edited_scene].version=version; + if (scene_idx < 0) { + edited_scene[current_edited_scene].version=version; + } else { + ERR_FAIL_INDEX(scene_idx,edited_scene.size()); + edited_scene[scene_idx].version=version; + } } @@ -758,10 +773,15 @@ void EditorData::set_edited_scene_import_metadata(Ref<ResourceImportMetadata> p_ } -Ref<ResourceImportMetadata> EditorData::get_edited_scene_import_metadata() const{ +Ref<ResourceImportMetadata> EditorData::get_edited_scene_import_metadata(int idx) const{ ERR_FAIL_INDEX_V(current_edited_scene,edited_scene.size(),Ref<ResourceImportMetadata>()); - return edited_scene[current_edited_scene].medatata; + if(idx<0) { + return edited_scene[current_edited_scene].medatata; + } else { + ERR_FAIL_INDEX_V(idx,edited_scene.size(),Ref<ResourceImportMetadata>()); + return edited_scene[idx].medatata; + } } diff --git a/tools/editor/editor_data.h b/tools/editor/editor_data.h index 319155655d..a0b716f560 100644 --- a/tools/editor/editor_data.h +++ b/tools/editor/editor_data.h @@ -156,6 +156,7 @@ public: void paste_object_params(Object *p_object); Dictionary get_editor_states() const; + Dictionary get_scene_editor_states(int p_idx) const; void set_editor_states(const Dictionary& p_states); void get_editor_breakpoints(List<String> *p_breakpoints); void clear_editor_states(); @@ -184,15 +185,15 @@ public: void set_edited_scene(int p_idx); void set_edited_scene_root(Node* p_root); void set_edited_scene_import_metadata(Ref<ResourceImportMetadata> p_mdata); - Ref<ResourceImportMetadata> get_edited_scene_import_metadata() const; + Ref<ResourceImportMetadata> get_edited_scene_import_metadata(int p_idx = -1) const; int get_edited_scene() const; - Node* get_edited_scene_root(); + Node* get_edited_scene_root(int p_idx = -1); int get_edited_scene_count() const; String get_scene_title(int p_idx) const; String get_scene_path(int p_idx) const; String get_scene_type(int p_idx) const; Ref<Script> get_scene_root_script(int p_idx) const; - void set_edited_scene_version(uint64_t version); + void set_edited_scene_version(uint64_t version, int p_scene_idx = -1); uint64_t get_edited_scene_version() const; uint64_t get_scene_version(int p_idx) const; void clear_edited_scenes(); diff --git a/tools/editor/editor_node.cpp b/tools/editor/editor_node.cpp index 89d7f8f3ce..80fadd18bd 100644 --- a/tools/editor/editor_node.cpp +++ b/tools/editor/editor_node.cpp @@ -725,9 +725,9 @@ void EditorNode::_get_scene_metadata(const String& p_file) { } -void EditorNode::_set_scene_metadata(const String& p_file) { +void EditorNode::_set_scene_metadata(const String& p_file, int p_idx) { - Node *scene = editor_data.get_edited_scene_root(); + Node *scene = editor_data.get_edited_scene_root(p_idx); if (!scene) return; @@ -740,7 +740,7 @@ void EditorNode::_set_scene_metadata(const String& p_file) { Ref<ConfigFile> cf; cf.instance(); - Dictionary md = editor_data.get_editor_states(); + Dictionary md = editor_data.get_edited_scene()==p_idx?editor_data.get_editor_states():editor_data.get_scene_editor_states(p_idx); List<Variant> keys; md.get_key_list(&keys); @@ -954,9 +954,9 @@ void EditorNode::_save_scene_with_preview(String p_file) { } -void EditorNode::_save_scene(String p_file) { +void EditorNode::_save_scene(String p_file, int idx) { - Node *scene = editor_data.get_edited_scene_root(); + Node *scene = editor_data.get_edited_scene_root(idx); if (!scene) { @@ -970,7 +970,7 @@ void EditorNode::_save_scene(String p_file) { editor_data.apply_changes_in_editors(); - _set_scene_metadata(p_file); + _set_scene_metadata(p_file,idx); Ref<PackedScene> sdata; @@ -1001,7 +1001,7 @@ void EditorNode::_save_scene(String p_file) { return; } - sdata->set_import_metadata(editor_data.get_edited_scene_import_metadata()); + sdata->set_import_metadata(editor_data.get_edited_scene_import_metadata(idx)); int flg=0; if (EditorSettings::get_singleton()->get("on_save/compress_binary_resources")) flg|=ResourceSaver::FLAG_COMPRESS; @@ -1017,7 +1017,10 @@ void EditorNode::_save_scene(String p_file) { if (err==OK) { scene->set_filename( Globals::get_singleton()->localize_path(p_file) ); //EditorFileSystem::get_singleton()->update_file(p_file,sdata->get_type()); - set_current_version(editor_data.get_undo_redo().get_version()); + if (idx < 0 || idx == editor_data.get_edited_scene()) + set_current_version(editor_data.get_undo_redo().get_version()); + else + editor_data.set_edited_scene_version(0,idx); _update_title(); _update_scene_tabs(); } else { @@ -1810,7 +1813,6 @@ void EditorNode::_run(bool p_current,const String& p_custom) { String args; - if (p_current || (editor_data.get_edited_scene_root() && p_custom==editor_data.get_edited_scene_root()->get_filename())) { Node *scene = editor_data.get_edited_scene_root(); @@ -1833,12 +1835,7 @@ void EditorNode::_run(bool p_current,const String& p_custom) { } - bool autosave = EDITOR_DEF("run/auto_save_before_running",true); - if (autosave) { - - _menu_option(FILE_SAVE_SCENE); - } if (run_settings_dialog->get_run_mode()==RunSettingsDialog::RUN_LOCAL_SCENE) { @@ -1911,7 +1908,7 @@ void EditorNode::_run(bool p_current,const String& p_custom) { _save_scene_with_preview(scene->get_filename()); } } - + _menu_option(FILE_SAVE_ALL_SCENES); editor_data.save_editor_external_data(); } @@ -2167,6 +2164,19 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { } break; + case FILE_SAVE_ALL_SCENES: { + for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { + Node *scene = editor_data.get_edited_scene_root(i); + if (scene && scene->get_filename()!="") { + // save in background if in the script editor + if (i != editor_data.get_edited_scene() || _get_current_main_editor() == EDITOR_SCRIPT) { + _save_scene(scene->get_filename(), i); + } else { + _save_scene_with_preview(scene->get_filename()); + } + }// else: ignore new scenes + } + } break; case FILE_SAVE_BEFORE_RUN: { if (!p_confirmed) { accept->get_ok()->set_text(TTR("Yes")); @@ -2494,7 +2504,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { #endif case RESOURCE_NEW: { - create_dialog->popup_centered_ratio(); + create_dialog->popup(true); } break; case RESOURCE_LOAD: { @@ -2665,11 +2675,16 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) { } break; case RUN_PLAY_NATIVE: { - _menu_option_confirm(RUN_STOP,true); - emit_signal("play_pressed"); - editor_run.run_native_notify(); - - + + bool autosave = EDITOR_DEF("run/auto_save_before_running",true); + if (autosave) { + _menu_option_confirm(FILE_SAVE_ALL_SCENES, false); + } + if (run_native->is_deploy_debug_remote_enabled()){ + _menu_option_confirm(RUN_STOP,true); + emit_signal("play_pressed"); + editor_run.run_native_notify(); + } } break; case RUN_SCENE_SETTINGS: { @@ -5648,6 +5663,7 @@ EditorNode::EditorNode() { p->add_separator(); p->add_shortcut(ED_SHORTCUT("editor/save_scene",TTR("Save Scene"),KEY_MASK_CMD+KEY_S),FILE_SAVE_SCENE); p->add_shortcut(ED_SHORTCUT("editor/save_scene_as",TTR("Save Scene As.."),KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_S),FILE_SAVE_AS_SCENE); + p->add_shortcut(ED_SHORTCUT("editor/save_all_scenes",TTR("Save all Scenes"),KEY_MASK_ALT+KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_S),FILE_SAVE_ALL_SCENES); p->add_separator(); p->add_shortcut(ED_SHORTCUT("editor/close_scene",TTR("Close Scene"),KEY_MASK_SHIFT+KEY_MASK_CTRL+KEY_W),FILE_CLOSE); p->add_separator(); diff --git a/tools/editor/editor_node.h b/tools/editor/editor_node.h index c4414d5c36..d18de1c531 100644 --- a/tools/editor/editor_node.h +++ b/tools/editor/editor_node.h @@ -124,6 +124,7 @@ private: FILE_OPEN_SCENE, FILE_SAVE_SCENE, FILE_SAVE_AS_SCENE, + FILE_SAVE_ALL_SCENES, FILE_SAVE_BEFORE_RUN, FILE_SAVE_AND_RUN, FILE_IMPORT_SUBSCENE, @@ -438,7 +439,7 @@ private: void _node_renamed(); void _editor_select(int p_which); - void _set_scene_metadata(const String &p_file); + void _set_scene_metadata(const String &p_file, int p_idx=-1); void _get_scene_metadata(const String& p_file); void _update_title(); void _update_scene_tabs(); @@ -448,7 +449,7 @@ private: void _rebuild_import_menu(); - void _save_scene(String p_file); + void _save_scene(String p_file, int idx = -1); void _instance_request(const String& p_path); diff --git a/tools/editor/editor_run_native.cpp b/tools/editor/editor_run_native.cpp index 234dd03087..edbcc71284 100644 --- a/tools/editor/editor_run_native.cpp +++ b/tools/editor/editor_run_native.cpp @@ -101,10 +101,8 @@ void EditorRunNative::_run_native(int p_idx,const String& p_platform) { Ref<EditorExportPlatform> eep = EditorImportExport::get_singleton()->get_export_platform(p_platform); ERR_FAIL_COND(eep.is_null()); - if (deploy_debug_remote) { - emit_signal("native_run"); + emit_signal("native_run"); - } int flags=0; if (deploy_debug_remote) flags|=EditorExportPlatform::EXPORT_REMOTE_DEBUG; diff --git a/tools/editor/plugins/spatial_editor_plugin.cpp b/tools/editor/plugins/spatial_editor_plugin.cpp index 91a347c69b..a70df78697 100644 --- a/tools/editor/plugins/spatial_editor_plugin.cpp +++ b/tools/editor/plugins/spatial_editor_plugin.cpp @@ -77,21 +77,30 @@ void SpatialEditorViewport::_update_camera() { String SpatialEditorGizmo::get_handle_name(int p_idx) const { + if (get_script_instance() && get_script_instance()->has_method("get_handle_name")) + return get_script_instance()->call("get_handle_name", p_idx); + return ""; } Variant SpatialEditorGizmo::get_handle_value(int p_idx) const{ + if (get_script_instance() && get_script_instance()->has_method("get_handle_value")) + return get_script_instance()->call("get_handle_value", p_idx); + return Variant(); } void SpatialEditorGizmo::set_handle(int p_idx,Camera *p_camera, const Point2& p_point) { + if (get_script_instance() && get_script_instance()->has_method("set_handle")) + get_script_instance()->call("set_handle", p_idx, p_camera, p_point); } void SpatialEditorGizmo::commit_handle(int p_idx,const Variant& p_restore,bool p_cancel){ - + if (get_script_instance() && get_script_instance()->has_method("commit_handle")) + get_script_instance()->call("commit_handle", p_idx, p_restore, p_cancel); } bool SpatialEditorGizmo::intersect_frustum(const Camera *p_camera,const Vector<Plane> &p_frustum) { diff --git a/tools/editor/plugins/tile_map_editor_plugin.cpp b/tools/editor/plugins/tile_map_editor_plugin.cpp index 9d27ac8aa3..d686c37d1a 100644 --- a/tools/editor/plugins/tile_map_editor_plugin.cpp +++ b/tools/editor/plugins/tile_map_editor_plugin.cpp @@ -412,6 +412,24 @@ void TileMapEditor::_draw_cell(int p_cell, const Point2i& p_point, bool p_flip_h if (node->get_tile_origin()==TileMap::TILE_ORIGIN_TOP_LEFT) { rect.pos+=tile_ofs; + } else if (node->get_tile_origin()==TileMap::TILE_ORIGIN_BOTTOM_LEFT) { + Size2 cell_size = node->get_cell_size(); + + rect.pos+=tile_ofs; + + if(p_transpose) + { + if(p_flip_h) + rect.pos.x-=cell_size.x; + else + rect.pos.x+=cell_size.x; + } else { + if(p_flip_v) + rect.pos.y-=cell_size.y; + else + rect.pos.y+=cell_size.y; + } + } else if (node->get_tile_origin()==TileMap::TILE_ORIGIN_CENTER) { rect.pos+=node->get_cell_size()/2; Vector2 s = r.size; diff --git a/tools/editor/plugins/tile_set_editor_plugin.cpp b/tools/editor/plugins/tile_set_editor_plugin.cpp index a2c7147bf3..39a15189e7 100644 --- a/tools/editor/plugins/tile_set_editor_plugin.cpp +++ b/tools/editor/plugins/tile_set_editor_plugin.cpp @@ -73,24 +73,24 @@ void TileSetEditor::_import_scene(Node *scene, Ref<TileSet> p_library, bool p_me p_library->tile_set_name(id,mi->get_name()); } - - p_library->tile_set_texture(id,texture); p_library->tile_set_material(id,material); Vector2 phys_offset; + Size2 s; - if (mi->is_centered()) { - Size2 s; - if (mi->is_region()) { - s=mi->get_region_rect().size; - } else { - s=texture->get_size(); - } - phys_offset+=-s/2; - } if (mi->is_region()) { + s=mi->get_region_rect().size; p_library->tile_set_region(id,mi->get_region_rect()); + } else { + const int frame = mi->get_frame(); + const int hframes = mi->get_hframes(); + s=texture->get_size()/Size2(hframes,mi->get_vframes()); + p_library->tile_set_region(id,Rect2(Vector2(frame%hframes,frame/hframes)*s,s)); + } + + if (mi->is_centered()) { + phys_offset+=-s/2; } Vector<Ref<Shape2D> >collisions; diff --git a/tools/editor/project_settings.cpp b/tools/editor/project_settings.cpp index 6822e50b73..6be1abf52f 100644 --- a/tools/editor/project_settings.cpp +++ b/tools/editor/project_settings.cpp @@ -100,16 +100,6 @@ void ProjectSettings::_notification(int p_what) { translation_res_file_open->add_filter("*."+E->get()); translation_res_option_file_open->add_filter("*."+E->get()); } - - List<String> afn; - ResourceLoader::get_recognized_extensions_for_type("Script",&afn); - ResourceLoader::get_recognized_extensions_for_type("PackedScene",&afn); - - for (List<String>::Element *E=afn.front();E;E=E->next()) { - - autoload_file_open->add_filter("*."+E->get()); - } - } } @@ -564,7 +554,7 @@ void ProjectSettings::popup_project_settings() { popup_centered_ratio(); globals_editor->update_category_list(); _update_translations(); - _update_autoload(); + autoload_settings->update_autoload(); plugin_settings->update_plugins(); } @@ -616,10 +606,26 @@ void ProjectSettings::_item_add() { String name = catname!="" ? catname+"/"+propname : propname; - Globals::get_singleton()->set(name,value); + undo_redo->create_action("Add Global Property"); + + undo_redo->add_do_property(Globals::get_singleton(), name, value); + undo_redo->add_do_method(Globals::get_singleton(), "set_persisting", name, true); + + if (Globals::get_singleton()->has(name)) { + undo_redo->add_undo_property(Globals::get_singleton(), name, Globals::get_singleton()->get(name)); + } else { + undo_redo->add_undo_property(Globals::get_singleton(), name, Variant()); + } + + undo_redo->add_do_method(globals_editor, "update_category_list"); + undo_redo->add_undo_method(globals_editor, "update_category_list"); + + undo_redo->add_do_method(this, "_settings_changed"); + undo_redo->add_undo_method(this, "_settings_changed"); + + undo_redo->commit_action(); globals_editor->set_current_section(catname); - globals_editor->update_category_list(); _settings_changed(); } @@ -633,10 +639,20 @@ void ProjectSettings::_item_del() { String name = catname!="" ? catname+"/"+propname : propname; - Globals::get_singleton()->set(name,Variant()); + undo_redo->create_action("Delete Global Property"); - globals_editor->set_current_section(catname); - globals_editor->update_category_list(); + undo_redo->add_do_property(Globals::get_singleton(), name, Variant()); + + undo_redo->add_undo_property(Globals::get_singleton(), name, Globals::get_singleton()->get(name)); + undo_redo->add_undo_method(Globals::get_singleton(), "set_persisting", name, Globals::get_singleton()->is_persisting(name)); + + undo_redo->add_do_method(globals_editor, "update_category_list"); + undo_redo->add_undo_method(globals_editor, "update_category_list"); + + undo_redo->add_do_method(this, "_settings_changed"); + undo_redo->add_undo_method(this, "_settings_changed"); + + undo_redo->commit_action(); _settings_changed(); } @@ -811,263 +827,6 @@ void ProjectSettings::_translation_file_open() { translation_file_open->popup_centered_ratio(); } - -void ProjectSettings::_autoload_file_callback(const String& p_path) { - - autoload_add_path->set_text(p_path); - //if (autoload_add_name->get_text().strip_edges()==String()) { - - autoload_add_name->set_text( p_path.get_file().basename() ); - //} - - //_translation_add(p_translation); -} - -void ProjectSettings::_autoload_file_open() { - - autoload_file_open->popup_centered_ratio(); -} - -void ProjectSettings::_autoload_edited() { - - if (updating_autoload) - return; - - TreeItem *ti = autoload_list->get_edited(); - int column = autoload_list->get_edited_column(); - - if (!ti || (column != 0 && column != 2)) - return; - - if (column == 0) { - String name = ti->get_text(0); - String old_name = selected_autoload.substr(selected_autoload.find("/")+1,selected_autoload.length()); - - if (!name.is_valid_identifier()) { - ti->set_text(0,old_name); - message->set_text(TTR("Invalid name.")+"\n"+TTR("Valid characters:")+" a-z, A-Z, 0-9 or _"); - message->popup_centered(Size2(300,100)); - return; - } - - if (ObjectTypeDB::type_exists(name)) { - ti->set_text(0,old_name); - message->set_text(TTR("Invalid name. Must not collide with an existing engine class name.")); - message->popup_centered(Size2(400,100)); - return; - } - - for(int i=0;i<Variant::VARIANT_MAX;i++) { - if (Variant::get_type_name(Variant::Type(i))==name) { - ti->set_text(0,old_name); - message->set_text(TTR("Invalid name. Must not collide with an existing buit-in type name.")); - message->popup_centered(Size2(400,100)); - return; - } - } - - for(int i=0;i<GlobalConstants::get_global_constant_count();i++) { - if (GlobalConstants::get_global_constant_name(i)==name) { - ti->set_text(0,old_name); - message->set_text(TTR("Invalid name. Must not collide with an existing global constant name.")); - message->popup_centered(Size2(400,100)); - return; - } - } - - if (Globals::get_singleton()->has("autoload/"+name)) { - ti->set_text(0,old_name); - message->set_text(vformat(TTR("Autoload '%s' already exists!"),name)); - message->popup_centered(Size2(300,100)); - return; - } - - updating_autoload = true; - - name = "autoload/"+name; - String path = Globals::get_singleton()->get(selected_autoload); - bool is_persisting = Globals::get_singleton()->is_persisting(selected_autoload); - int order = Globals::get_singleton()->get_order(selected_autoload); - - undo_redo->create_action(TTR("Rename Autoload")); - undo_redo->add_do_property(Globals::get_singleton(),name,path); - undo_redo->add_do_method(Globals::get_singleton(),"set_persisting",name,is_persisting); - undo_redo->add_do_method(Globals::get_singleton(),"set_order",name,order); - undo_redo->add_do_method(Globals::get_singleton(),"clear",selected_autoload); - undo_redo->add_undo_property(Globals::get_singleton(),selected_autoload,path); - undo_redo->add_undo_method(Globals::get_singleton(),"set_persisting",selected_autoload,is_persisting); - undo_redo->add_undo_method(Globals::get_singleton(),"set_order",selected_autoload,order); - undo_redo->add_undo_method(Globals::get_singleton(),"clear",name); - undo_redo->add_do_method(this,"_update_autoload"); - undo_redo->add_undo_method(this,"_update_autoload"); - undo_redo->add_do_method(this,"_settings_changed"); - undo_redo->add_undo_method(this,"_settings_changed"); - undo_redo->commit_action(); - - selected_autoload = name; - } else if (column == 2) { - updating_autoload = true; - - bool checked = ti->is_checked(2); - String base = "autoload/"+ti->get_text(0); - String path = Globals::get_singleton()->get(base); - int order = Globals::get_singleton()->get_order(base); - - if (path.begins_with("*")) - path = path.substr(1,path.length()); - - if (checked) - path = "*" + path; - - undo_redo->create_action(TTR("Toggle AutoLoad Globals")); - undo_redo->add_do_property(Globals::get_singleton(),base,path); - undo_redo->add_undo_property(Globals::get_singleton(),base,Globals::get_singleton()->get(base)); - undo_redo->add_do_method(Globals::get_singleton(),"set_order",base,order); // keep order, as config order matters for these - undo_redo->add_undo_method(Globals::get_singleton(),"set_order",base,order); - undo_redo->add_do_method(this,"_update_autoload"); - undo_redo->add_undo_method(this,"_update_autoload"); - undo_redo->add_do_method(this,"_settings_changed"); - undo_redo->add_undo_method(this,"_settings_changed"); - undo_redo->commit_action(); - } - - updating_autoload = false; -} - -void ProjectSettings::_autoload_add() { - - String name = autoload_add_name->get_text(); - if (!name.is_valid_identifier()) { - message->set_text(TTR("Invalid name.")+"\n"+TTR("Valid characters:")+" a-z, A-Z, 0-9 or _"); - message->popup_centered(Size2(300,100)); - return; - - } - - if (ObjectTypeDB::type_exists(name)) { - - message->set_text(TTR("Invalid name. Must not collide with an existing engine class name.")); - message->popup_centered(Size2(300,100)); - return; - - } - - for(int i=0;i<Variant::VARIANT_MAX;i++) { - if (Variant::get_type_name(Variant::Type(i))==name) { - - message->set_text(TTR("Invalid name. Must not collide with an existing buit-in type name.")); - message->popup_centered(Size2(300,100)); - return; - - } - } - - for(int i=0;i<GlobalConstants::get_global_constant_count();i++) { - - if (GlobalConstants::get_global_constant_name(i)==name) { - - message->set_text(TTR("Invalid name. Must not collide with an existing global constant name.")); - message->popup_centered(Size2(300,100)); - return; - } - - } - - String path = autoload_add_path->get_text(); - if (!FileAccess::exists(path)) { - message->set_text("Invalid Path.\nFile does not exist."); - message->popup_centered(Size2(300,100)); - return; - - } - if (!path.begins_with("res://")) { - message->set_text("Invalid Path.\nNot in resource path."); - message->popup_centered(Size2(300,100)); - return; - - } - - undo_redo->create_action(TTR("Add Autoload")); - name = "autoload/"+name; - undo_redo->add_do_property(Globals::get_singleton(),name,"*"+path); - if (Globals::get_singleton()->has(name)) - undo_redo->add_undo_property(Globals::get_singleton(),name,Globals::get_singleton()->get(name)); - else - undo_redo->add_undo_property(Globals::get_singleton(),name,Variant()); - - undo_redo->add_do_method(Globals::get_singleton(),"set_persisting",name,true); - undo_redo->add_do_method(this,"_update_autoload"); - undo_redo->add_undo_method(this,"_update_autoload"); - undo_redo->add_do_method(this,"_settings_changed"); - undo_redo->add_undo_method(this,"_settings_changed"); - undo_redo->commit_action(); - - autoload_add_path->set_text(""); - autoload_add_name->set_text(""); - - //autoload_file_open->popup_centered_ratio(); -} - -void ProjectSettings::_autoload_delete(Object *p_item,int p_column, int p_button) { - - - TreeItem *ti=p_item->cast_to<TreeItem>(); - String name = "autoload/"+ti->get_text(0); - - if (p_button==0) { - //delete - int order = Globals::get_singleton()->get_order(name); - undo_redo->create_action(TTR("Remove Autoload")); - undo_redo->add_do_property(Globals::get_singleton(),name,Variant()); - undo_redo->add_undo_property(Globals::get_singleton(),name,Globals::get_singleton()->get(name)); - undo_redo->add_undo_method(Globals::get_singleton(),"set_persisting",name,true); - undo_redo->add_undo_method(Globals::get_singleton(),"set_order",name,order); - undo_redo->add_do_method(this,"_update_autoload"); - undo_redo->add_undo_method(this,"_update_autoload"); - undo_redo->add_do_method(this,"_settings_changed"); - undo_redo->add_undo_method(this,"_settings_changed"); - undo_redo->commit_action(); - } else { - - TreeItem *swap = NULL; - - if (p_button==1) { - swap=ti->get_prev(); - } else if (p_button==2) { - swap=ti->get_next(); - } - if (!swap) - return; - - String swap_name= "autoload/"+swap->get_text(0); - - int order = Globals::get_singleton()->get_order(name); - int swap_order = Globals::get_singleton()->get_order(swap_name); - - undo_redo->create_action(TTR("Move Autoload")); - undo_redo->add_do_method(Globals::get_singleton(),"set_order",swap_name,order); - undo_redo->add_do_method(Globals::get_singleton(),"set_order",name,swap_order); - undo_redo->add_undo_method(Globals::get_singleton(),"set_order",swap_name,swap_order); - undo_redo->add_undo_method(Globals::get_singleton(),"set_order",name,order); - undo_redo->add_do_method(this,"_update_autoload"); - undo_redo->add_undo_method(this,"_update_autoload"); - undo_redo->add_do_method(this,"_settings_changed"); - undo_redo->add_undo_method(this,"_settings_changed"); - undo_redo->commit_action(); - - } - -} - -void ProjectSettings::_autoload_selected() { - TreeItem *ti = autoload_list->get_selected(); - - if (!ti) - return; - - selected_autoload = "autoload/"+ti->get_text(0); -} - void ProjectSettings::_translation_delete(Object *p_item,int p_column, int p_button) { TreeItem *ti = p_item->cast_to<TreeItem>(); @@ -1393,55 +1152,6 @@ void ProjectSettings::_update_translations() { } -void ProjectSettings::_update_autoload() { - - if (updating_autoload) - return; - - updating_autoload=true; - - autoload_list->clear(); - TreeItem *root = autoload_list->create_item(); - autoload_list->set_hide_root(true); - - List<PropertyInfo> props; - Globals::get_singleton()->get_property_list(&props); - - for(List<PropertyInfo>::Element *E=props.front();E;E=E->next()) { - - const PropertyInfo &pi=E->get(); - if (!pi.name.begins_with("autoload/")) - continue; - - String name = pi.name.get_slice("/",1); - String path = Globals::get_singleton()->get(pi.name); - - if (name=="") - continue; - bool global=false; - if (path.begins_with("*")) { - path=path.substr(1,path.length()); - global=true; - } - TreeItem *t = autoload_list->create_item(root); - t->set_text(0,name); - t->set_editable(0,true); - t->set_text(1,path); - t->set_cell_mode(2,TreeItem::CELL_MODE_CHECK); - t->set_editable(2,true); - t->set_text(2,TTR("Enable")); - t->set_checked(2,global); - t->add_button(3,get_icon("MoveUp","EditorIcons"),1); - t->add_button(3,get_icon("MoveDown","EditorIcons"),2); - t->add_button(3,get_icon("Del","EditorIcons"),0); - - - } - - updating_autoload=false; - -} - void ProjectSettings::_toggle_search_bar(bool p_pressed) { globals_editor->get_property_editor()->set_use_filter(p_pressed); @@ -1508,14 +1218,6 @@ void ProjectSettings::_bind_methods() { ObjectTypeDB::bind_method(_MD("_translation_res_delete"),&ProjectSettings::_translation_res_delete); ObjectTypeDB::bind_method(_MD("_translation_res_option_delete"),&ProjectSettings::_translation_res_option_delete); - ObjectTypeDB::bind_method(_MD("_autoload_add"),&ProjectSettings::_autoload_add); - ObjectTypeDB::bind_method(_MD("_autoload_file_open"),&ProjectSettings::_autoload_file_open); - ObjectTypeDB::bind_method(_MD("_autoload_file_callback"),&ProjectSettings::_autoload_file_callback); - ObjectTypeDB::bind_method(_MD("_update_autoload"),&ProjectSettings::_update_autoload); - ObjectTypeDB::bind_method(_MD("_autoload_delete"),&ProjectSettings::_autoload_delete); - ObjectTypeDB::bind_method(_MD("_autoload_edited"),&ProjectSettings::_autoload_edited); - ObjectTypeDB::bind_method(_MD("_autoload_selected"),&ProjectSettings::_autoload_selected); - ObjectTypeDB::bind_method(_MD("_clear_search_box"),&ProjectSettings::_clear_search_box); ObjectTypeDB::bind_method(_MD("_toggle_search_bar"),&ProjectSettings::_toggle_search_bar); @@ -1858,69 +1560,10 @@ ProjectSettings::ProjectSettings(EditorData *p_data) { { - VBoxContainer *avb = memnew( VBoxContainer ); - tab_container->add_child(avb); - avb->set_name(TTR("AutoLoad")); - HBoxContainer *ahb = memnew( HBoxContainer); - avb->add_child(ahb); - - - VBoxContainer *avb_path = memnew( VBoxContainer ); - avb_path->set_h_size_flags(SIZE_EXPAND_FILL); - HBoxContainer *ahb_path = memnew( HBoxContainer ); - autoload_add_path = memnew(LineEdit); - autoload_add_path->set_h_size_flags(SIZE_EXPAND_FILL); - ahb_path->add_child(autoload_add_path); - Button *browseaa = memnew( Button("..") ); - ahb_path->add_child(browseaa); - browseaa->connect("pressed",this,"_autoload_file_open"); - - avb_path->add_margin_child(TTR("Path:"),ahb_path); - ahb->add_child(avb_path); - - VBoxContainer *avb_name = memnew( VBoxContainer ); - avb_name->set_h_size_flags(SIZE_EXPAND_FILL); - - HBoxContainer *ahb_name = memnew( HBoxContainer ); - autoload_add_name = memnew(LineEdit); - autoload_add_name->set_h_size_flags(SIZE_EXPAND_FILL); - ahb_name->add_child(autoload_add_name); - avb_name->add_margin_child(TTR("Node Name:"),ahb_name); - Button *addaa = memnew( Button(TTR("Add")) ); - ahb_name->add_child(addaa); - addaa->connect("pressed",this,"_autoload_add"); - - ahb->add_child(avb_name); - - autoload_list = memnew( Tree ); - autoload_list->set_v_size_flags(SIZE_EXPAND_FILL); - avb->add_margin_child(TTR("List:"),autoload_list,true); - - autoload_file_open=memnew( EditorFileDialog ); - add_child(autoload_file_open); - autoload_file_open->set_mode(EditorFileDialog::MODE_OPEN_FILE); - autoload_file_open->connect("file_selected",this,"_autoload_file_callback"); - - autoload_list->set_columns(4); - autoload_list->set_column_titles_visible(true); - autoload_list->set_column_title(0,TTR("Name")); - autoload_list->set_column_expand(0,true); - autoload_list->set_column_min_width(0,100); - autoload_list->set_column_title(1,TTR("Path")); - autoload_list->set_column_expand(1,true); - autoload_list->set_column_min_width(1,100); - autoload_list->set_column_title(2,TTR("Singleton")); - autoload_list->set_column_expand(2,false); - autoload_list->set_column_min_width(2,80); - autoload_list->set_column_expand(3,false); - autoload_list->set_column_min_width(3,80); - - autoload_list->connect("button_pressed",this,"_autoload_delete"); - autoload_list->connect("item_edited",this,"_autoload_edited"); - autoload_list->connect("cell_selected", this, "_autoload_selected"); - - updating_autoload=false; - + autoload_settings = memnew( EditorAutoloadSettings ); + autoload_settings->set_name(TTR("AutoLoad")); + tab_container->add_child(autoload_settings); + autoload_settings->connect("autoload_changed", this, "_settings_changed"); } { diff --git a/tools/editor/project_settings.h b/tools/editor/project_settings.h index 79e1acf75e..46e98f69ad 100644 --- a/tools/editor/project_settings.h +++ b/tools/editor/project_settings.h @@ -34,6 +34,7 @@ #include "undo_redo.h" #include "editor_data.h" #include "scene/gui/tab_container.h" +#include "editor_autoload_settings.h" #include "editor_plugin_settings.h" //#include "project_export_settings.h" @@ -88,26 +89,10 @@ class ProjectSettings : public AcceptDialog { Tree *translation_remap; Tree *translation_remap_options; - - Tree *autoload_list; - String selected_autoload; - EditorFileDialog *autoload_file_open; - LineEdit *autoload_add_name; - LineEdit *autoload_add_path; - + EditorAutoloadSettings *autoload_settings; EditorPluginSettings *plugin_settings; - void _update_autoload(); - void _autoload_file_callback(const String& p_path); - void _autoload_add(); - void _autoload_edited(); - void _autoload_file_open(); - void _autoload_delete(Object *p_item,int p_column, int p_button); - void _autoload_selected(); - bool updating_autoload; - - void _item_selected(); void _item_adds(String); void _item_add(); diff --git a/tools/editor/property_editor.cpp b/tools/editor/property_editor.cpp index 7816dd9bc7..ceb62d5ff0 100644 --- a/tools/editor/property_editor.cpp +++ b/tools/editor/property_editor.cpp @@ -4187,6 +4187,8 @@ public: void SectionedPropertyEditor::_bind_methods() { ObjectTypeDB::bind_method("_section_selected",&SectionedPropertyEditor::_section_selected); + + ObjectTypeDB::bind_method("update_category_list", &SectionedPropertyEditor::update_category_list); } void SectionedPropertyEditor::_section_selected(int p_which) { diff --git a/tools/editor/scene_tree_dock.cpp b/tools/editor/scene_tree_dock.cpp index 4526fa26aa..2e7d65eadc 100644 --- a/tools/editor/scene_tree_dock.cpp +++ b/tools/editor/scene_tree_dock.cpp @@ -243,7 +243,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { //if (!_validate_no_foreign()) // break; - create_dialog->popup_centered_ratio(); + create_dialog->popup(true); } break; case TOOL_INSTANCE: { @@ -281,7 +281,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } break; case TOOL_REPLACE: { - create_dialog->popup_centered_ratio(); + create_dialog->popup(false); } break; case TOOL_CONNECT: { diff --git a/tools/editor/spatial_editor_gizmos.cpp b/tools/editor/spatial_editor_gizmos.cpp index edc2fc513d..480d33fd0a 100644 --- a/tools/editor/spatial_editor_gizmos.cpp +++ b/tools/editor/spatial_editor_gizmos.cpp @@ -623,12 +623,11 @@ void EditorSpatialGizmo::_bind_methods() { ObjectTypeDB::bind_method(_MD("add_unscaled_billboard","material:Material","default_scale"),&EditorSpatialGizmo::add_unscaled_billboard,DEFVAL(1)); ObjectTypeDB::bind_method(_MD("add_handles","handles","billboard","secondary"),&EditorSpatialGizmo::add_handles,DEFVAL(false),DEFVAL(false)); ObjectTypeDB::bind_method(_MD("set_spatial_node","node:Spatial"),&EditorSpatialGizmo::_set_spatial_node); + ObjectTypeDB::bind_method(_MD("clear"),&EditorSpatialGizmo::clear); BIND_VMETHOD( MethodInfo("redraw")); BIND_VMETHOD( MethodInfo(Variant::STRING,"get_handle_name",PropertyInfo(Variant::INT,"index"))); - { - BIND_VMETHOD( MethodInfo("get_handle_value:Variant",PropertyInfo(Variant::INT,"index"))); - } + BIND_VMETHOD( MethodInfo("get_handle_value:Variant",PropertyInfo(Variant::INT,"index"))); BIND_VMETHOD( MethodInfo("set_handle",PropertyInfo(Variant::INT,"index"),PropertyInfo(Variant::OBJECT,"camera:Camera"),PropertyInfo(Variant::VECTOR2,"point"))); MethodInfo cm = MethodInfo("commit_handle",PropertyInfo(Variant::INT,"index"),PropertyInfo(Variant::NIL,"restore:Variant"),PropertyInfo(Variant::BOOL,"cancel")); cm.default_arguments.push_back(false); diff --git a/tools/export/blender25/godot_export_manager.py b/tools/export/blender25/godot_export_manager.py deleted file mode 100644 index c91f55b51c..0000000000 --- a/tools/export/blender25/godot_export_manager.py +++ /dev/null @@ -1,576 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# Script copyright (c) Andreas Esau - -import bpy -from bpy.props import (StringProperty, BoolProperty, EnumProperty, - FloatProperty, IntProperty, CollectionProperty) -import os -from bpy.app.handlers import persistent -from mathutils import Matrix - -bl_info = { - "name": "Godot Export Manager", - "author": "Andreas Esau", - "version": (1, 0), - "blender": (2, 7, 0), - "location": "Scene Properties > Godot Export Manager", - "description": "Godot Export Manager uses the Better Collada Exporter" - "to manage Export Groups and automatically export the objects groups" - "to Collada Files.", - "warning": "", - "wiki_url": ("http://www.godotengine.org"), - "tracker_url": "", - "category": "Import-Export"} - - -class godot_export_manager(bpy.types.Panel): - bl_label = "Godot Export Manager" - bl_space_type = 'PROPERTIES' - bl_region_type = 'WINDOW' - bl_context = "scene" - - bpy.types.Scene.godot_export_on_save = BoolProperty(default=False) - - def draw(self, context): - """ Draw function for all ui elements """ - layout = self.layout - split = self.layout.split() - scene = bpy.data.scenes[0] - ob = context.object - scene = context.scene - - row = layout.row() - col = row.column() - col.prop(scene, "godot_export_on_save", text="Export Groups on save") - - row = layout.row() - col = row.column(align=True) - op = col.operator("scene.godot_add_objects_to_group", - text="Add selected objects to Group", - icon="COPYDOWN") - - op = col.operator("scene.godot_delete_objects_from_group", - text="Delete selected objects from Group", - icon="PASTEDOWN") - - row = layout.row() - col = row.column() - col.label(text="Export Groups:") - - row = layout.row() - col = row.column() - - col.template_list("UI_List_Godot", "dummy", scene, - "godot_export_groups", scene, - "godot_export_groups_index", rows=1, maxrows=10, - type='DEFAULT') - - col = row.column(align=True) - col.operator("scene.godot_add_export_group", text="", icon="ZOOMIN") - col.operator("scene.godot_delete_export_group", text="", - icon="ZOOMOUT") - col.operator("scene.godot_export_all_groups", text="", icon="EXPORT") - - if len(scene.godot_export_groups) > 0: - row = layout.row() - col = row.column() - group = scene.godot_export_groups[scene.godot_export_groups_index] - col.prop(group, "name", text="Group Name") - col.prop(group, "export_name", text="Export Name") - col.prop(group, "export_path", text="Export Filepath") - - row = layout.row() - col = row.column() - row = layout.row() - col = row.column() - col.label(text="Export Settings:") - - col = col.row(align=True) - col.prop(group, "apply_loc", toggle=True, icon="MAN_TRANS") - col.prop(group, "apply_rot", toggle=True, icon="MAN_ROT") - col.prop(group, "apply_scale", toggle=True, icon="MAN_SCALE") - - row = layout.row() - col = row.column() - - col.prop(group, "use_include_particle_duplicates") - col.prop(group, "use_mesh_modifiers") - col.prop(group, "use_tangent_arrays") - col.prop(group, "use_triangles") - col.prop(group, "use_copy_images") - col.prop(group, "use_active_layers") - col.prop(group, "use_anim") - col.prop(group, "use_anim_action_all") - col.prop(group, "use_anim_skip_noexp") - col.prop(group, "use_anim_optimize") - col.prop(group, "anim_optimize_precision") - col.prop(group, "use_metadata") - - -class UI_List_Godot(bpy.types.UIList): - """ Custom template_list look """ - def draw_item(self, context, layout, data, item, icon, active_data, - active_propname, index): - ob = data - slot = item - col = layout.row(align=True) - - col.label(text=item.name, icon="GROUP") - col.prop(item, "active", text="") - - op = col.operator("scene.godot_select_group_objects", - text="", emboss=False, icon="RESTRICT_SELECT_OFF") - op.idx = index - op = col.operator("scene.godot_export_group", text="", emboss=False, - icon="EXPORT") - op.idx = index - - -class add_objects_to_group(bpy.types.Operator): - bl_idname = "scene.godot_add_objects_to_group" - bl_label = "Add Objects to Group" - bl_description = "Adds the selected Objects to the active group below." - - undo = BoolProperty(default=True) - - def execute(self, context): - scene = context.scene - - objects_str = "" - if len(scene.godot_export_groups) > 0: - for i, object in enumerate(context.selected_objects): - if object.name not in scene.godot_export_groups[ - scene.godot_export_groups_index].nodes: - node = scene.godot_export_groups[ - scene.godot_export_groups_index].nodes.add() - node.name = object.name - if i == 0: - objects_str += object.name - else: - objects_str += ", "+object.name - - self.report({'INFO'}, objects_str + " added to group.") - if self.undo: - bpy.ops.ed.undo_push(message="Objects added to group") - else: - self.report({'WARNING'}, "Create a group first.") - - return {'FINISHED'} - - -class del_objects_from_group(bpy.types.Operator): - bl_idname = "scene.godot_delete_objects_from_group" - bl_label = "Delete Objects from Group" - bl_description = "Delets the selected Objects from the active group below." - - def execute(self, context): - scene = context.scene - - if len(scene.godot_export_groups) > 0: - - selected_objects = [] - for object in context.selected_objects: - selected_objects.append(object.name) - - objects_str = "" - j = 0 - for i, node in enumerate(scene.godot_export_groups[ - scene.godot_export_groups_index].nodes): - if node.name in selected_objects: - scene.godot_export_groups[ - scene.godot_export_groups_index].nodes.remove(i) - - if j == 0: - objects_str += object.name - else: - objects_str += ", "+object.name - j += 1 - - self.report({'INFO'}, objects_str + " deleted from group.") - bpy.ops.ed.undo_push(message="Objects deleted from group") - else: - self.report({'WARNING'}, "There is no group to delete from.") - - return {'FINISHED'} - - -class select_group_objects(bpy.types.Operator): - bl_idname = "scene.godot_select_group_objects" - bl_label = "Select Group Objects" - bl_description = "Will select all group Objects in the scene." - - idx = IntProperty() - - def execute(self, context): - scene = context.scene - for object in context.scene.objects: - object.select = False - for node in scene.godot_export_groups[self.idx].nodes: - if node.name in bpy.data.objects: - bpy.data.objects[node.name].select = True - context.scene.objects.active = bpy.data.objects[node.name] - - return {'FINISHED'} - - -class export_groups_autosave(bpy.types.Operator): - bl_idname = "scene.godot_export_groups_autosave" - bl_label = "Export All Groups" - bl_description = "Exports all groups to Collada." - - def execute(self, context): - scene = context.scene - if scene.godot_export_on_save: - for i in range(len(scene.godot_export_groups)): - if scene.godot_export_groups[i].active: - bpy.ops.scene.godot_export_group(idx=i) - self.report({'INFO'}, "All Groups exported.") - bpy.ops.ed.undo_push(message="Export all Groups") - - return {'FINISHED'} - - -class export_all_groups(bpy.types.Operator): - bl_idname = "scene.godot_export_all_groups" - bl_label = "Export All Groups" - bl_description = "Exports all groups to Collada." - - def execute(self, context): - scene = context.scene - - for i in range(0, len(scene.godot_export_groups)): - bpy.ops.scene.godot_export_group(idx=i, export_all=True) - - self.report({'INFO'}, "All Groups exported.") - - return {'FINISHED'} - - -class export_group(bpy.types.Operator): - bl_idname = "scene.godot_export_group" - bl_label = "Export Group" - bl_description = "Exports the active group to destination folder"\ - " as Collada file." - - idx = IntProperty(default=0) - export_all = BoolProperty(default=False) - - def copy_object_recursive(self, ob, parent, single_user=True): - new_ob = bpy.data.objects[ob.name].copy() - if single_user or ob.type == "ARMATURE": - new_mesh_data = new_ob.data.copy() - new_ob.data = new_mesh_data - bpy.context.scene.objects.link(new_ob) - - if ob != parent: - new_ob.parent = parent - else: - new_ob.parent = None - - for child in ob.children: - self.copy_object_recursive(child, new_ob, single_user) - new_ob.select = True - - return new_ob - - def delete_object(self, ob): - if ob is not None: - for child in ob.children: - self.delete_object(child) - bpy.context.scene.objects.unlink(ob) - bpy.data.objects.remove(ob) - - def convert_group_to_node(self, group): - if group.dupli_group is not None: - for object in group.dupli_group.objects: - if object.parent is None: - object = self.copy_object_recursive(object, object, True) - matrix = Matrix(object.matrix_local) - object.matrix_local = Matrix() - object.matrix_local *= group.matrix_local - object.matrix_local *= matrix - - self.delete_object(group) - - def execute(self, context): - - scene = context.scene - group = context.scene.godot_export_groups - - if not group[self.idx].active and self.export_all: - return{'FINISHED'} - - for i, object in enumerate(group[self.idx].nodes): - if object.name in bpy.data.objects: - pass - else: - group[self.idx].nodes.remove(i) - bpy.ops.ed.undo_push(message="Clear not existent Group Nodes.") - - path = group[self.idx].export_path - if (path.find("//") == 0 or path.find("\\\\") == 0): - # If relative, convert to absolute - path = bpy.path.abspath(path) - path = path.replace("\\", "/") - - # If path exists and group export name is set the group will be - # exported - if os.path.exists(path) and group[self.idx].export_name != "": - - context.scene.layers = [True] * 20 - - if group[self.idx].export_name.endswith(".dae"): - path = os.path.join(path, group[self.idx].export_name) - else: - path = os.path.join(path, group[self.idx].export_name+".dae") - - hide_select = [] - for object in context.scene.objects: - hide_select.append(object.hide_select) - object.hide_select = False - object.select = False - context.scene.objects.active = None - - # Make particle duplicates, parent and select them - nodes_to_be_added = [] - if group[self.idx].use_include_particle_duplicates: - for i, object in enumerate(group[self.idx].nodes): - if bpy.data.objects[object.name].type != "EMPTY": - context.scene.objects.active = bpy.data.objects[ - object.name] - bpy.data.objects[object.name].select = True - bpy.ops.object.duplicates_make_real() - for object in context.selected_objects: - nodes_to_be_added.append(object) - bpy.ops.object.parent_set(type="OBJECT", - keep_transform=False) - - for object in context.selected_objects: - object.select = False - bpy.data.objects[object.name].select = False - context.scene.objects.active = None - for object in nodes_to_be_added: - object.select = True - - # Select all other nodes from the group - for i, object in enumerate(group[self.idx].nodes): - if bpy.data.objects[object.name].type == "EMPTY": - self.convert_group_to_node(bpy.data.objects[object.name]) - else: - bpy.data.objects[object.name].select = True - - bpy.ops.object.transform_apply(location=group[self.idx].apply_loc, - rotation=group[self.idx].apply_rot, - scale=group[self.idx].apply_scale) - bpy.ops.export_scene.dae( - check_existing=True, filepath=path, filter_glob="*.dae", - object_types=group[self.idx].object_types, - use_export_selected=group[self.idx].use_export_selected, - use_mesh_modifiers=group[self.idx].use_mesh_modifiers, - use_tangent_arrays=group[self.idx].use_tangent_arrays, - use_triangles=group[self.idx].use_triangles, - use_copy_images=group[self.idx].use_copy_images, - use_active_layers=group[self.idx].use_active_layers, - use_anim=group[self.idx].use_anim, - use_anim_action_all=group[self.idx].use_anim_action_all, - use_anim_skip_noexp=group[self.idx].use_anim_skip_noexp, - use_anim_optimize=group[self.idx].use_anim_optimize, - anim_optimize_precision=group[ - self.idx].anim_optimize_precision, - use_metadata=group[self.idx].use_metadata) - - self.report({'INFO'}, - '"' + group[self.idx].name + '" Group exported.') - msg = "Export Group "+group[self.idx].name - - bpy.ops.ed.undo_push(message="") - bpy.ops.ed.undo() - bpy.ops.ed.undo_push(message=msg) - - else: - self.report({'INFO'}, "Define Export Name and Export Path.") - - return{'FINISHED'} - - -class add_export_group(bpy.types.Operator): - bl_idname = "scene.godot_add_export_group" - bl_label = "Adds a new export Group" - bl_description = "Creates a new Export Group with the selected"\ - " Objects assigned to it." - - def execute(self, context): - scene = context.scene - - item = scene.godot_export_groups.add() - item.name = "New Group" - for object in context.selected_objects: - node = item.nodes.add() - node.name = object.name - scene.godot_export_groups_index = len(scene.godot_export_groups)-1 - bpy.ops.ed.undo_push(message="Create New Export Group") - - return{'FINISHED'} - - -class del_export_group(bpy.types.Operator): - bl_idname = "scene.godot_delete_export_group" - bl_label = "Delets the selected export Group" - bl_description = "Delets the active Export Group." - - def invoke(self, context, event): - wm = context.window_manager - - return wm.invoke_confirm(self, event) - - def execute(self, context): - scene = context.scene - - scene.godot_export_groups.remove(scene.godot_export_groups_index) - if scene.godot_export_groups_index > 0: - scene.godot_export_groups_index -= 1 - bpy.ops.ed.undo_push(message="Delete Export Group") - - return {'FINISHED'} - - -class godot_node_list(bpy.types.PropertyGroup): - name = StringProperty() - - -class godot_export_groups(bpy.types.PropertyGroup): - name = StringProperty(name="Group Name") - export_name = StringProperty(name="scene_name") - nodes = CollectionProperty(type=godot_node_list) - export_path = StringProperty(subtype="DIR_PATH") - active = BoolProperty(default=True, description="Export Group") - - object_types = EnumProperty(name="Object Types", options={'ENUM_FLAG'}, - items=(('EMPTY', "Empty", ""), - ('CAMERA', "Camera", ""), - ('LAMP', "Lamp", ""), - ('ARMATURE', "Armature", ""), - ('MESH', "Mesh", ""), - ('CURVE', "Curve", ""), ), - default={'EMPTY', 'CAMERA', 'LAMP', - 'ARMATURE', 'MESH', 'CURVE'}) - - apply_scale = BoolProperty(name="Apply Scale", - description="Apply Scale before export.", - default=False) - apply_rot = BoolProperty(name="Apply Rotation", - description="Apply Rotation before export.", - default=False) - apply_loc = BoolProperty(name="Apply Location", - description="Apply Location before export.", - default=False) - - use_export_selected = BoolProperty(name="Selected Objects", - description="Export only selected" - "objects (and visible in active layers " - "if that applies).", default=True) - use_mesh_modifiers = BoolProperty(name="Apply Modifiers", - description="Apply modifiers to mesh" - " objects (on a copy!).", default=True) - use_tangent_arrays = BoolProperty(name="Tangent Arrays", - description="Export Tangent and Binormal" - " arrays (for normalmapping).", - default=False) - use_triangles = BoolProperty(name="Triangulate", - description="Export Triangles instead of" - " Polygons.", default=False) - - use_copy_images = BoolProperty(name="Copy Images", - description="Copy Images (create images/ " - "subfolder)", default=False) - use_active_layers = BoolProperty(name="Active Layers", - description="Export only objects on the" - " active layers.", default=True) - use_anim = BoolProperty(name="Export Animation", - description="Export keyframe animation", - default=False) - use_anim_action_all = BoolProperty(name="All Actions", - description=("Export all actions for " - "the first armature found" - " in separate DAE files"), - default=False) - use_anim_skip_noexp = BoolProperty(name="Skip (-noexp) Actions", - description="Skip exporting of" - " actions whose name end in (-noexp)." - " Useful to skip control animations.", - default=True) - use_anim_optimize = BoolProperty(name="Optimize Keyframes", - description="Remove double keyframes", - default=True) - - anim_optimize_precision = FloatProperty( - name="Precision", description=("Tolerence for comparing double " - "keyframes (higher for greater " - "accuracy)"), min=1, max=16, - soft_min=1, soft_max=16, default=6.0) - - use_metadata = BoolProperty(name="Use Metadata", default=True, - options={'HIDDEN'}) - use_include_particle_duplicates = BoolProperty( - name="Include Particle Duplicates", default=True) - - -def register(): - bpy.utils.register_class(godot_export_manager) - bpy.utils.register_class(godot_node_list) - bpy.utils.register_class(godot_export_groups) - bpy.utils.register_class(add_export_group) - bpy.utils.register_class(del_export_group) - bpy.utils.register_class(export_all_groups) - bpy.utils.register_class(export_groups_autosave) - bpy.utils.register_class(export_group) - bpy.utils.register_class(add_objects_to_group) - bpy.utils.register_class(del_objects_from_group) - bpy.utils.register_class(select_group_objects) - bpy.utils.register_class(UI_List_Godot) - - bpy.types.Scene.godot_export_groups = CollectionProperty( - type=godot_export_groups) - bpy.types.Scene.godot_export_groups_index = IntProperty(default=0, min=0) - - -def unregister(): - bpy.utils.unregister_class(godot_export_manager) - bpy.utils.unregister_class(godot_node_list) - bpy.utils.unregister_class(godot_export_groups) - bpy.utils.unregister_class(export_groups_autosave) - bpy.utils.unregister_class(add_export_group) - bpy.utils.unregister_class(del_export_group) - bpy.utils.unregister_class(export_all_groups) - bpy.utils.unregister_class(export_group) - bpy.utils.unregister_class(add_objects_to_group) - bpy.utils.unregister_class(del_objects_from_group) - bpy.utils.unregister_class(select_group_objects) - bpy.utils.unregister_class(UI_List_Godot) - - -@persistent -def auto_export(dummy): - bpy.ops.scene.godot_export_groups_autosave() - -bpy.app.handlers.save_post.append(auto_export) - -if __name__ == "__main__": - register() diff --git a/tools/export/blender25/install.txt b/tools/export/blender25/install.txt deleted file mode 100644 index 049af8848e..0000000000 --- a/tools/export/blender25/install.txt +++ /dev/null @@ -1,11 +0,0 @@ -Godot Author's Own Collada Exporter ------------------------------------ - -1) Copy the "io_scene_dae" directory to wherever blender stores the - scripts/addons folder (You will see many other io_scene_blahblah like - folders). Copy the entire dir, not just the contents, make it just like - the others. -2) Go to Blender settings and enable the "Better Collada" plugin -3) Enjoy proper Collada export. -4) If it's broken, contact us. -
\ No newline at end of file diff --git a/tools/export/blender25/io_scene_dae/__init__.py b/tools/export/blender25/io_scene_dae/__init__.py deleted file mode 100644 index 0b82e1537b..0000000000 --- a/tools/export/blender25/io_scene_dae/__init__.py +++ /dev/null @@ -1,192 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8-80 compliant> - -import bpy -from bpy.props import StringProperty, BoolProperty, FloatProperty, EnumProperty - -from bpy_extras.io_utils import ExportHelper -bl_info = { - "name": "Better Collada Exporter", - "author": "Juan Linietsky", - "blender": (2, 5, 8), - "api": 38691, - "location": "File > Import-Export", - "description": ("Export DAE Scenes, This plugin actually works better! " - "otherwise contact me."), - "warning": "", - "wiki_url": ("http://www.godotengine.org"), - "tracker_url": "", - "support": 'OFFICIAL', - "category": "Import-Export"} - -if "bpy" in locals(): - import imp - if "export_dae" in locals(): - imp.reload(export_dae) - - -class ExportDAE(bpy.types.Operator, ExportHelper): - '''Selection to DAE''' - bl_idname = "export_scene.dae" - bl_label = "Export DAE" - bl_options = {'PRESET'} - - filename_ext = ".dae" - filter_glob = StringProperty(default="*.dae", options={'HIDDEN'}) - - # List of operator properties, the attributes will be assigned - # to the class instance from the operator settings before calling. - - object_types = EnumProperty( - name="Object Types", - options={'ENUM_FLAG'}, - items=(('EMPTY', "Empty", ""), - ('CAMERA', "Camera", ""), - ('LAMP', "Lamp", ""), - ('ARMATURE', "Armature", ""), - ('MESH', "Mesh", ""), - ('CURVE', "Curve", ""), - ), - default={'EMPTY', 'CAMERA', 'LAMP', 'ARMATURE', 'MESH', 'CURVE'}, - ) - - use_export_selected = BoolProperty( - name="Selected Objects", - description="Export only selected objects (and visible in active " - "layers if that applies).", - default=False, - ) - use_mesh_modifiers = BoolProperty( - name="Apply Modifiers", - description="Apply modifiers to mesh objects (on a copy!).", - default=False, - ) - use_tangent_arrays = BoolProperty( - name="Tangent Arrays", - description="Export Tangent and Binormal arrays " - "(for normalmapping).", - default=False, - ) - use_triangles = BoolProperty( - name="Triangulate", - description="Export Triangles instead of Polygons.", - default=False, - ) - - use_copy_images = BoolProperty( - name="Copy Images", - description="Copy Images (create images/ subfolder)", - default=False, - ) - use_active_layers = BoolProperty( - name="Active Layers", - description="Export only objects on the active layers.", - default=True, - ) - use_anim = BoolProperty( - name="Export Animation", - description="Export keyframe animation", - default=False, - ) - use_anim_action_all = BoolProperty( - name="All Actions", - description=("Export all actions for the first armature found " - "in separate DAE files"), - default=False, - ) - use_anim_skip_noexp = BoolProperty( - name="Skip (-noexp) Actions", - description="Skip exporting of actions whose name end in (-noexp)." - " Useful to skip control animations.", - default=True, - ) - use_anim_optimize = BoolProperty( - name="Optimize Keyframes", - description="Remove double keyframes", - default=True, - ) - - anim_optimize_precision = FloatProperty( - name="Precision", - description=("Tolerence for comparing double keyframes " - "(higher for greater accuracy)"), - min=1, max=16, - soft_min=1, soft_max=16, - default=6.0, - ) - - use_metadata = BoolProperty( - name="Use Metadata", - default=True, - options={'HIDDEN'}, - ) - - @property - def check_extension(self): - # return self.batch_mode == 'OFF' - return True - - def check(self, context): - return True - """ - isretur_def_change = super().check(context) - return (is_xna_change or is_def_change) - """ - - def execute(self, context): - if not self.filepath: - raise Exception("filepath not set") - - """ global_matrix = Matrix() - - global_matrix[0][0] = \ - global_matrix[1][1] = \ - global_matrix[2][2] = self.global_scale - """ - - keywords = self.as_keywords(ignore=("axis_forward", - "axis_up", - "global_scale", - "check_existing", - "filter_glob", - "xna_validate", - )) - - from . import export_dae - return export_dae.save(self, context, **keywords) - - -def menu_func(self, context): - self.layout.operator(ExportDAE.bl_idname, text="Better Collada (.dae)") - - -def register(): - bpy.utils.register_module(__name__) - - bpy.types.INFO_MT_file_export.append(menu_func) - - -def unregister(): - bpy.utils.unregister_module(__name__) - - bpy.types.INFO_MT_file_export.remove(menu_func) - -if __name__ == "__main__": - register() diff --git a/tools/export/blender25/io_scene_dae/export_dae.py b/tools/export/blender25/io_scene_dae/export_dae.py deleted file mode 100644 index 9f8458c0da..0000000000 --- a/tools/export/blender25/io_scene_dae/export_dae.py +++ /dev/null @@ -1,1923 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# <pep8 compliant> - -# Script copyright (C) Juan Linietsky -# Contact Info: juan@codenix.com - -""" -This script is an exporter to the Khronos Collada file format. - -http://www.khronos.org/collada/ -""" - -# TODO: -# Materials & Textures -# Optionally export Vertex Colors -# Morph Targets -# Control bone removal -# Copy textures -# Export Keyframe Optimization -# -- -# Morph Targets -# Blender native material? (?) - -import os -import time -import math # math.pi -import shutil -import bpy -import bmesh -from mathutils import Vector, Matrix - -# According to collada spec, order matters -S_ASSET = 0 -S_IMGS = 1 -S_FX = 2 -S_MATS = 3 -S_GEOM = 4 -S_MORPH = 5 -S_SKIN = 6 -S_CONT = 7 -S_CAMS = 8 -S_LAMPS = 9 -S_ANIM_CLIPS = 10 -S_NODES = 11 -S_ANIM = 12 - -CMP_EPSILON = 0.0001 - - -def snap_tup(tup): - ret = () - for x in tup: - ret += (x - math.fmod(x, 0.0001), ) - - return tup - - -def strmtx(mtx): - s = " " - for x in range(4): - for y in range(4): - s += str(mtx[x][y]) - s += " " - s += " " - return s - - -def numarr(a, mult=1.0): - s = " " - for x in a: - s += " " + str(x * mult) - s += " " - return s - - -def numarr_alpha(a, mult=1.0): - s = " " - for x in a: - s += " " + str(x * mult) - if len(a) == 3: - s += " 1.0" - s += " " - return s - - -def strarr(arr): - s = " " - for x in arr: - s += " " + str(x) - s += " " - return s - - -class DaeExporter: - - def validate_id(self, d): - if (d.find("id-") == 0): - return "z" + d - return d - - def new_id(self, t): - self.last_id += 1 - return "id-" + t + "-" + str(self.last_id) - - class Vertex: - - def close_to(self, v): - if self.vertex - v.vertex.length() > CMP_EPSILON: - return False - if self.normal - v.normal.length() > CMP_EPSILON: - return False - if self.uv - v.uv.length() > CMP_EPSILON: - return False - if self.uv2 - v.uv2.length() > CMP_EPSILON: - return False - - return True - - def get_tup(self): - tup = (self.vertex.x, self.vertex.y, self.vertex.z, self.normal.x, - self.normal.y, self.normal.z) - for t in self.uv: - tup = tup + (t.x, t.y) - if self.color is not None: - tup = tup + (self.color.x, self.color.y, self.color.z) - if self.tangent is not None: - tup = tup + (self.tangent.x, self.tangent.y, self.tangent.z) - if self.bitangent is not None: - tup = tup + (self.bitangent.x, self.bitangent.y, - self.bitangent.z) - for t in self.bones: - tup = tup + (float(t), ) - for t in self.weights: - tup = tup + (float(t), ) - - return tup - - def __init__(self): - self.vertex = Vector((0.0, 0.0, 0.0)) - self.normal = Vector((0.0, 0.0, 0.0)) - self.tangent = None - self.bitangent = None - self.color = None - self.uv = [] - self.uv2 = Vector((0.0, 0.0)) - self.bones = [] - self.weights = [] - - def writel(self, section, indent, text): - if (not (section in self.sections)): - self.sections[section] = [] - line = "" - for x in range(indent): - line += "\t" - line += text - self.sections[section].append(line) - - def export_image(self, image): - if (image in self.image_cache): - return self.image_cache[image] - - imgpath = image.filepath - if (imgpath.find("//") == 0 or imgpath.find("\\\\") == 0): - # If relative, convert to absolute - imgpath = bpy.path.abspath(imgpath) - - # Path is absolute, now do something! - - if (self.config["use_copy_images"]): - # copy image - basedir = os.path.dirname(self.path) + "/images" - if (not os.path.isdir(basedir)): - os.makedirs(basedir) - - if os.path.isfile(imgpath): - dstfile = basedir + "/" + os.path.basename(imgpath) - - if not os.path.isfile(dstfile): - shutil.copy(imgpath, dstfile) - imgpath = "images/" + os.path.basename(imgpath) - else: - # If file is not found save it as png file in the destination - # folder - img_tmp_path = image.filepath - if img_tmp_path.endswith((".bmp", ".rgb", ".png", ".jpeg", - ".jpg", ".jp2", ".tga", ".cin", - ".dpx", ".exr", ".hdr", ".tif")): - image.filepath = basedir + "/" + \ - os.path.basename(img_tmp_path) - else: - image.filepath = basedir + "/" + image.name + ".png" - - dstfile = basedir + "/" + os.path.basename(image.filepath) - - if not os.path.isfile(dstfile): - image.save() - imgpath = "images/" + os.path.basename(image.filepath) - image.filepath = img_tmp_path - - else: - # Export relative, always, no one wants absolute paths. - try: - # Export unix compatible always - imgpath = os.path.relpath( - imgpath, os.path.dirname(self.path)).replace("\\", "/") - - except: - # Fails sometimes, not sure why - pass - - imgid = self.new_id("image") - - print("FOR: " + imgpath) - -# if (not os.path.isfile(imgpath)): -# print("NOT FILE?") -# if imgpath.endswith((".bmp", ".rgb", ".png", ".jpeg", ".jpg", -# ".jp2", ".tga", ".cin", ".dpx", ".exr", -# ".hdr", ".tif")): -# imgpath="images/"+os.path.basename(imgpath) -# else: -# imgpath="images/"+image.name+".png" - - self.writel(S_IMGS, 1, '<image id="' + imgid + - '" name="' + image.name + '">') - self.writel(S_IMGS, 2, '<init_from>' + imgpath + '</init_from>') - self.writel(S_IMGS, 1, '</image>') - self.image_cache[image] = imgid - return imgid - - def export_material(self, material, double_sided_hint=True): - if (material in self.material_cache): - return self.material_cache[material] - - fxid = self.new_id("fx") - self.writel(S_FX, 1, '<effect id="' + fxid + '" name="' + - material.name + '-fx">') - self.writel(S_FX, 2, '<profile_COMMON>') - - # Find and fetch the textures and create sources - sampler_table = {} - diffuse_tex = None - specular_tex = None - emission_tex = None - normal_tex = None - for i in range(len(material.texture_slots)): - ts = material.texture_slots[i] - if not ts: - continue - if not ts.use: - continue - if not ts.texture: - continue - if ts.texture.type != "IMAGE": - continue - - if ts.texture.image is None: - continue - - # Image - imgid = self.export_image(ts.texture.image) - - # Surface - surface_sid = self.new_id("fx_surf") - self.writel(S_FX, 3, '<newparam sid="' + surface_sid + '">') - self.writel(S_FX, 4, '<surface type="2D">') - # This is sooo weird - self.writel(S_FX, 5, '<init_from>' + imgid + '</init_from>') - self.writel(S_FX, 5, '<format>A8R8G8B8</format>') - self.writel(S_FX, 4, '</surface>') - self.writel(S_FX, 3, '</newparam>') - # Sampler, collada sure likes it difficult - sampler_sid = self.new_id("fx_sampler") - self.writel(S_FX, 3, '<newparam sid="' + sampler_sid + '">') - self.writel(S_FX, 4, '<sampler2D>') - self.writel(S_FX, 5, '<source>' + surface_sid + '</source>') - self.writel(S_FX, 4, '</sampler2D>') - self.writel(S_FX, 3, '</newparam>') - sampler_table[i] = sampler_sid - - if ts.use_map_color_diffuse and diffuse_tex is None: - diffuse_tex = sampler_sid - if ts.use_map_color_spec and specular_tex is None: - specular_tex = sampler_sid - if ts.use_map_emit and emission_tex is None: - emission_tex = sampler_sid - if ts.use_map_normal and normal_tex is None: - normal_tex = sampler_sid - - self.writel(S_FX, 3, '<technique sid="common">') - shtype = "blinn" - self.writel(S_FX, 4, '<' + shtype + '>') - # Ambient? from where? - - self.writel(S_FX, 5, '<emission>') - if emission_tex is not None: - self.writel(S_FX, 6, '<texture texture="' + emission_tex + - '" texcoord="CHANNEL1"/>') - else: - self.writel(S_FX, 6, '<color>' + - numarr_alpha(material.diffuse_color, material.emit) + - ' </color>') # not totally right but good enough - self.writel(S_FX, 5, '</emission>') - - self.writel(S_FX, 5, '<ambient>') - self.writel(S_FX, 6, '<color>' + - numarr_alpha(self.scene.world.ambient_color, - material.ambient) + ' </color>') - self.writel(S_FX, 5, '</ambient>') - - self.writel(S_FX, 5, '<diffuse>') - if diffuse_tex is not None: - self.writel(S_FX, 6, '<texture texture="' + diffuse_tex + - '" texcoord="CHANNEL1"/>') - else: - self.writel(S_FX, 6, - '<color>' + numarr_alpha(material.diffuse_color, - material.diffuse_intensity) + - '</color>') - self.writel(S_FX, 5, '</diffuse>') - - self.writel(S_FX, 5, '<specular>') - if specular_tex is not None: - self.writel(S_FX, 6, '<texture texture="' + specular_tex + - '" texcoord="CHANNEL1"/>') - else: - self.writel(S_FX, 6, '<color>' + numarr_alpha( - material.specular_color, material.specular_intensity) + - '</color>') - self.writel(S_FX, 5, '</specular>') - - self.writel(S_FX, 5, '<shininess>') - self.writel(S_FX, 6, '<float>' + str(material.specular_hardness) + - '</float>') - self.writel(S_FX, 5, '</shininess>') - - self.writel(S_FX, 5, '<reflective>') - self.writel(S_FX, 6, '<color>' + numarr_alpha(material.mirror_color) + - '</color>') - self.writel(S_FX, 5, '</reflective>') - - if (material.use_transparency): - self.writel(S_FX, 5, '<transparency>') - self.writel(S_FX, 6, '<float>' + str(material.alpha) + '</float>') - self.writel(S_FX, 5, '</transparency>') - - self.writel(S_FX, 5, '<index_of_refraction>') - self.writel(S_FX, 6, '<float>' + str(material.specular_ior) + - '</float>') - self.writel(S_FX, 5, '</index_of_refraction>') - - self.writel(S_FX, 4, '</' + shtype + '>') - - self.writel(S_FX, 4, '<extra>') - self.writel(S_FX, 5, '<technique profile="FCOLLADA">') - if (normal_tex): - self.writel(S_FX, 6, '<bump bumptype="NORMALMAP">') - self.writel(S_FX, 7, '<texture texture="' + normal_tex + - '" texcoord="CHANNEL1"/>') - self.writel(S_FX, 6, '</bump>') - - self.writel(S_FX, 5, '</technique>') - self.writel(S_FX, 5, '<technique profile="GOOGLEEARTH">') - self.writel(S_FX, 6, '<double_sided>' + ["0", "1"][double_sided_hint] + - "</double_sided>") - self.writel(S_FX, 5, '</technique>') - - if (material.use_shadeless): - self.writel(S_FX, 5, '<technique profile="GODOT">') - self.writel(S_FX, 6, '<unshaded>1</unshaded>') - self.writel(S_FX, 5, '</technique>') - - self.writel(S_FX, 4, '</extra>') - - self.writel(S_FX, 3, '</technique>') - self.writel(S_FX, 2, '</profile_COMMON>') - self.writel(S_FX, 1, '</effect>') - - # Also export blender material in all it's glory (if set as active) - - # Material - matid = self.new_id("material") - self.writel(S_MATS, 1, '<material id="' + matid + '" name="' + - material.name + '">') - self.writel(S_MATS, 2, '<instance_effect url="#' + fxid + '"/>') - self.writel(S_MATS, 1, '</material>') - - self.material_cache[material] = matid - return matid - - def export_mesh(self, node, armature=None, skeyindex=-1, skel_source=None, - custom_name=None): - mesh = node.data - - if (node.data in self.mesh_cache): - return self.mesh_cache[mesh] - - if (skeyindex == -1 and mesh.shape_keys is not None and len( - mesh.shape_keys.key_blocks)): - values = [] - morph_targets = [] - md = None - for k in range(0, len(mesh.shape_keys.key_blocks)): - shape = node.data.shape_keys.key_blocks[k] - values += [shape.value] # save value - shape.value = 0 - - mid = self.new_id("morph") - - for k in range(0, len(mesh.shape_keys.key_blocks)): - - shape = node.data.shape_keys.key_blocks[k] - node.show_only_shape_key = True - node.active_shape_key_index = k - shape.value = 1.0 - mesh.update() - """ - oldval = shape.value - shape.value = 1.0 - - """ - p = node.data - v = node.to_mesh(bpy.context.scene, True, "RENDER") - node.data = v -# self.export_node(node, il, shape.name) - node.data.update() - if (armature and k == 0): - md = self.export_mesh(node, armature, k, mid, shape.name) - else: - md = self.export_mesh(node, None, k, None, shape.name) - - node.data = p - node.data.update() - shape.value = 0.0 - morph_targets.append(md) - - """ - shape.value = oldval - """ - node.show_only_shape_key = False - node.active_shape_key_index = 0 - - self.writel(S_MORPH, 1, '<controller id="' + mid + '" name="">') - # if ("skin_id" in morph_targets[0]): - # self.writel(S_MORPH, 2, '<morph source="#'+morph_targets[0][ - # "skin_id"]+'" method="NORMALIZED">') - # else: - self.writel(S_MORPH, 2, '<morph source="#' + - morph_targets[0]["id"] + '" method="NORMALIZED">') - - self.writel(S_MORPH, 3, '<source id="' + mid + '-morph-targets">') - self.writel(S_MORPH, 4, '<IDREF_array id="' + mid + - '-morph-targets-array" count="' + - str(len(morph_targets) - 1) + '">') - marr = "" - warr = "" - for i in range(len(morph_targets)): - if (i == 0): - continue - elif (i > 1): - marr += " " - - if ("skin_id" in morph_targets[i]): - marr += morph_targets[i]["skin_id"] - else: - marr += morph_targets[i]["id"] - - warr += " 0" - - self.writel(S_MORPH, 5, marr) - self.writel(S_MORPH, 4, '</IDREF_array>') - self.writel(S_MORPH, 4, '<technique_common>') - self.writel(S_MORPH, 5, '<accessor source="#' + mid + - '-morph-targets-array" count="' + - str(len(morph_targets) - 1) + '" stride="1">') - self.writel( - S_MORPH, 6, '<param name="MORPH_TARGET" type="IDREF"/>') - self.writel(S_MORPH, 5, '</accessor>') - self.writel(S_MORPH, 4, '</technique_common>') - self.writel(S_MORPH, 3, '</source>') - - self.writel(S_MORPH, 3, '<source id="' + mid + '-morph-weights">') - self.writel(S_MORPH, 4, '<float_array id="' + mid + - '-morph-weights-array" count="' + - str(len(morph_targets) - 1) + '" >') - self.writel(S_MORPH, 5, warr) - self.writel(S_MORPH, 4, '</float_array>') - self.writel(S_MORPH, 4, '<technique_common>') - self.writel(S_MORPH, 5, '<accessor source="#' + mid + - '-morph-weights-array" count="' + - str(len(morph_targets) - 1) + '" stride="1">') - self.writel( - S_MORPH, 6, '<param name="MORPH_WEIGHT" type="float"/>') - self.writel(S_MORPH, 5, '</accessor>') - self.writel(S_MORPH, 4, '</technique_common>') - self.writel(S_MORPH, 3, '</source>') - - self.writel(S_MORPH, 3, '<targets>') - self.writel( - S_MORPH, 4, '<input semantic="MORPH_TARGET" source="#' + mid + - '-morph-targets"/>') - self.writel( - S_MORPH, 4, '<input semantic="MORPH_WEIGHT" source="#' + mid + - '-morph-weights"/>') - self.writel(S_MORPH, 3, '</targets>') - self.writel(S_MORPH, 2, '</morph>') - self.writel(S_MORPH, 1, '</controller>') - if armature is not None: - - self.armature_for_morph[node] = armature - - meshdata = {} - if (armature): - meshdata = morph_targets[0] - meshdata["morph_id"] = mid - else: - meshdata["id"] = morph_targets[0]["id"] - meshdata["morph_id"] = mid - meshdata["material_assign"] = morph_targets[ - 0]["material_assign"] - - self.mesh_cache[node.data] = meshdata - return meshdata - - apply_modifiers = len(node.modifiers) and self.config[ - "use_mesh_modifiers"] - - name_to_use = mesh.name - # print("name to use: "+mesh.name) - if (custom_name is not None and custom_name != ""): - name_to_use = custom_name - - mesh = node.to_mesh(self.scene, apply_modifiers, - "RENDER") # is this allright? - - triangulate = self.config["use_triangles"] - if (triangulate): - bm = bmesh.new() - bm.from_mesh(mesh) - bmesh.ops.triangulate(bm, faces=bm.faces) - bm.to_mesh(mesh) - bm.free() - - mesh.update(calc_tessface=True) - vertices = [] - vertex_map = {} - surface_indices = {} - materials = {} - - materials = {} - - si = None - if armature is not None: - si = self.skeleton_info[armature] - - has_uv = False - has_uv2 = False - has_weights = armature is not None - has_tangents = self.config["use_tangent_arrays"] # could detect.. - has_colors = len(mesh.vertex_colors) - mat_assign = [] - - uv_layer_count = len(mesh.uv_textures) - if has_tangents and len(mesh.uv_textures): - try: - mesh.calc_tangents() - except: - self.operator.report( - {'WARNING'}, 'CalcTangets failed for mesh "' + mesh.name + - '", no tangets will be exported.') - # uv_layer_count=0 - mesh.calc_normals_split() - has_tangents = False - - else: - mesh.calc_normals_split() - has_tangents = False - - for fi in range(len(mesh.polygons)): - f = mesh.polygons[fi] - - if not (f.material_index in surface_indices): - surface_indices[f.material_index] = [] - # print("Type: " + str(type(f.material_index))) - # print("IDX: " + str(f.material_index) + "/" + str( - # len(mesh.materials))) - - try: - # Bizarre blender behavior i don't understand, - # so catching exception - mat = mesh.materials[f.material_index] - except: - mat = None - - if (mat is not None): - materials[f.material_index] = self.export_material( - mat, mesh.show_double_sided) - else: - # weird, has no material? - materials[f.material_index] = None - - indices = surface_indices[f.material_index] - vi = [] - # Vertices always 3 - """ - if (len(f.vertices)==3): - vi.append(0) - vi.append(1) - vi.append(2) - elif (len(f.vertices)==4): - #todo, should use shortest path - vi.append(0) - vi.append(1) - vi.append(2) - vi.append(0) - vi.append(2) - vi.append(3) - """ - - for lt in range(f.loop_total): - loop_index = f.loop_start + lt - ml = mesh.loops[loop_index] - mv = mesh.vertices[ml.vertex_index] - - v = self.Vertex() - v.vertex = Vector(mv.co) - - for xt in mesh.uv_layers: - v.uv.append(Vector(xt.data[loop_index].uv)) - - if (has_colors): - v.color = Vector( - mesh.vertex_colors[0].data[loop_index].color) - - v.normal = Vector(ml.normal) - - if (has_tangents): - v.tangent = Vector(ml.tangent) - v.bitangent = Vector(ml.bitangent) - - # if (armature): - # v.vertex = node.matrix_world * v.vertex - - # v.color=Vertex(mv. ??? - - if armature is not None: - wsum = 0.0 - zero_bones = [] - - for vg in mv.groups: - if vg.group >= len(node.vertex_groups): - continue - name = node.vertex_groups[vg.group].name - - if (name in si["bone_index"]): - # could still put the weight as 0.0001 maybe - # blender has a lot of zero weight stuff - if (vg.weight > 0.001): - v.bones.append(si["bone_index"][name]) - v.weights.append(vg.weight) - wsum += vg.weight - if (wsum == 0.0): - if not self.wrongvtx_report: - self.operator.report( - {'WARNING'}, 'Mesh for object "' + node.name + - '" has unassigned weights. ' - 'This may look wrong in exported model.') - self.wrongvtx_report = True - - # blender can have bones assigned that weight zero - # so they remain local - # this is the best it can be done? - v.bones.append(0) - v.weights.append(1) - - tup = v.get_tup() - idx = 0 - # do not optmize if using shapekeys - if (skeyindex == -1 and tup in vertex_map): - idx = vertex_map[tup] - else: - idx = len(vertices) - vertices.append(v) - vertex_map[tup] = idx - - vi.append(idx) - - if (len(vi) > 2): - # only triangles and above - indices.append(vi) - - meshid = self.new_id("mesh") - self.writel(S_GEOM, 1, '<geometry id="' + meshid + - '" name="' + name_to_use + '">') - - self.writel(S_GEOM, 2, '<mesh>') - - # Vertex Array - self.writel(S_GEOM, 3, '<source id="' + meshid + '-positions">') - float_values = "" - for v in vertices: - float_values += " " + str(v.vertex.x) + " " + \ - str(v.vertex.y) + " " + str(v.vertex.z) - self.writel(S_GEOM, 4, '<float_array id="' + meshid + - '-positions-array" count="' + - str(len(vertices) * 3) + '">' + float_values + - '</float_array>') - self.writel(S_GEOM, 4, '<technique_common>') - self.writel(S_GEOM, 4, '<accessor source="#' + meshid + - '-positions-array" count="' + str(len(vertices)) + - '" stride="3">') - self.writel(S_GEOM, 5, '<param name="X" type="float"/>') - self.writel(S_GEOM, 5, '<param name="Y" type="float"/>') - self.writel(S_GEOM, 5, '<param name="Z" type="float"/>') - self.writel(S_GEOM, 4, '</accessor>') - self.writel(S_GEOM, 4, '</technique_common>') - self.writel(S_GEOM, 3, '</source>') - - # Normal Array - - self.writel(S_GEOM, 3, '<source id="' + meshid + '-normals">') - float_values = "" - for v in vertices: - float_values += " " + str(v.normal.x) + " " + \ - str(v.normal.y) + " " + str(v.normal.z) - self.writel(S_GEOM, 4, '<float_array id="' + meshid + - '-normals-array" count="' + - str(len(vertices) * 3) + '">' + float_values + - '</float_array>') - self.writel(S_GEOM, 4, '<technique_common>') - self.writel(S_GEOM, 4, '<accessor source="#' + meshid + - '-normals-array" count="' + str(len(vertices)) + - '" stride="3">') - self.writel(S_GEOM, 5, '<param name="X" type="float"/>') - self.writel(S_GEOM, 5, '<param name="Y" type="float"/>') - self.writel(S_GEOM, 5, '<param name="Z" type="float"/>') - self.writel(S_GEOM, 4, '</accessor>') - self.writel(S_GEOM, 4, '</technique_common>') - self.writel(S_GEOM, 3, '</source>') - - if (has_tangents): - self.writel(S_GEOM, 3, '<source id="' + meshid + '-tangents">') - float_values = "" - for v in vertices: - float_values += " " + \ - str(v.tangent.x) + " " + \ - str(v.tangent.y) + " " + str(v.tangent.z) - self.writel(S_GEOM, 4, '<float_array id="' + meshid + - '-tangents-array" count="' + - str(len(vertices) * 3) + '">' + float_values + - '</float_array>') - self.writel(S_GEOM, 4, '<technique_common>') - self.writel(S_GEOM, 4, '<accessor source="#' + meshid + - '-tangents-array" count="' + str(len(vertices)) + - '" stride="3">') - self.writel(S_GEOM, 5, '<param name="X" type="float"/>') - self.writel(S_GEOM, 5, '<param name="Y" type="float"/>') - self.writel(S_GEOM, 5, '<param name="Z" type="float"/>') - self.writel(S_GEOM, 4, '</accessor>') - self.writel(S_GEOM, 4, '</technique_common>') - self.writel(S_GEOM, 3, '</source>') - - self.writel(S_GEOM, 3, '<source id="' + meshid + '-bitangents">') - float_values = "" - for v in vertices: - float_values += " " + \ - str(v.bitangent.x) + " " + \ - str(v.bitangent.y) + " " + str(v.bitangent.z) - self.writel(S_GEOM, 4, '<float_array id="' + meshid + - '-bitangents-array" count="' + - str(len(vertices) * 3) + '">' + float_values + - '</float_array>') - self.writel(S_GEOM, 4, '<technique_common>') - self.writel(S_GEOM, 4, '<accessor source="#' + meshid + - '-bitangents-array" count="' + str(len(vertices)) + - '" stride="3">') - self.writel(S_GEOM, 5, '<param name="X" type="float"/>') - self.writel(S_GEOM, 5, '<param name="Y" type="float"/>') - self.writel(S_GEOM, 5, '<param name="Z" type="float"/>') - self.writel(S_GEOM, 4, '</accessor>') - self.writel(S_GEOM, 4, '</technique_common>') - self.writel(S_GEOM, 3, '</source>') - - # UV Arrays - - for uvi in range(uv_layer_count): - - self.writel(S_GEOM, 3, '<source id="' + meshid + - '-texcoord-' + str(uvi) + '">') - float_values = "" - for v in vertices: - try: - float_values += " " + \ - str(v.uv[uvi].x) + " " + str(v.uv[uvi].y) - except: - # I don't understand this weird multi-uv-layer API, but - # with this it seems to works - float_values += " 0 0 " - - self.writel(S_GEOM, 4, '<float_array id="' + meshid + - '-texcoord-' + str(uvi) + - '-array" count="' + str(len(vertices) * 2) + '">' + - float_values + '</float_array>') - self.writel(S_GEOM, 4, '<technique_common>') - self.writel(S_GEOM, 4, '<accessor source="#' + meshid + - '-texcoord-' + - str(uvi) + '-array" count="' + str(len(vertices)) + - '" stride="2">') - self.writel(S_GEOM, 5, '<param name="S" type="float"/>') - self.writel(S_GEOM, 5, '<param name="T" type="float"/>') - self.writel(S_GEOM, 4, '</accessor>') - self.writel(S_GEOM, 4, '</technique_common>') - self.writel(S_GEOM, 3, '</source>') - - # Color Arrays - - if (has_colors): - self.writel(S_GEOM, 3, '<source id="' + meshid + '-colors">') - float_values = "" - for v in vertices: - float_values += " " + \ - str(v.color.x) + " " + \ - str(v.color.y) + " " + str(v.color.z) - self.writel(S_GEOM, 4, '<float_array id="' + meshid + - '-colors-array" count="' + - str(len(vertices) * 3) + '">' + float_values + - '</float_array>') - self.writel(S_GEOM, 4, '<technique_common>') - self.writel(S_GEOM, 4, '<accessor source="#' + meshid + - '-colors-array" count="' + str(len(vertices)) + - '" stride="3">') - self.writel(S_GEOM, 5, '<param name="X" type="float"/>') - self.writel(S_GEOM, 5, '<param name="Y" type="float"/>') - self.writel(S_GEOM, 5, '<param name="Z" type="float"/>') - self.writel(S_GEOM, 4, '</accessor>') - self.writel(S_GEOM, 4, '</technique_common>') - self.writel(S_GEOM, 3, '</source>') - - # Triangle Lists - self.writel(S_GEOM, 3, '<vertices id="' + meshid + '-vertices">') - self.writel( - S_GEOM, 4, '<input semantic="POSITION" source="#' + meshid + - '-positions"/>') - self.writel(S_GEOM, 3, '</vertices>') - - prim_type = "" - if (triangulate): - prim_type = "triangles" - else: - prim_type = "polygons" - - for m in surface_indices: - indices = surface_indices[m] - mat = materials[m] - - if (mat is not None): - matref = self.new_id("trimat") - self.writel(S_GEOM, 3, '<' + prim_type + ' count="' + str( - int(len(indices))) + '" material="' + matref + - '">') # todo material - mat_assign.append((mat, matref)) - else: - self.writel(S_GEOM, 3, '<' + prim_type + ' count="' + - str(int(len(indices))) + '">') # todo material - - self.writel(S_GEOM, 4, '<input semantic="VERTEX" source="#' + - meshid + '-vertices" offset="0"/>') - self.writel(S_GEOM, 4, '<input semantic="NORMAL" source="#' + - meshid + '-normals" offset="0"/>') - - for uvi in range(uv_layer_count): - self.writel(S_GEOM, 4, '<input semantic="TEXCOORD" source="#' + - meshid + - '-texcoord-' + str(uvi) + '" offset="0" set="' + - str(uvi) + '"/>') - - if (has_colors): - self.writel(S_GEOM, 4, '<input semantic="COLOR" source="#' + - meshid + '-colors" offset="0"/>') - if (has_tangents): - self.writel(S_GEOM, 4, - '<input semantic="TEXTANGENT" source="#' + - meshid + '-tangents" offset="0"/>') - self.writel(S_GEOM, 4, - '<input semantic="TEXBINORMAL" source="#' + - meshid + '-bitangents" offset="0"/>') - - if (triangulate): - int_values = "<p>" - for p in indices: - for i in p: - int_values += " " + str(i) - int_values += " </p>" - self.writel(S_GEOM, 4, int_values) - else: - for p in indices: - int_values = "<p>" - for i in p: - int_values += " " + str(i) - int_values += " </p>" - self.writel(S_GEOM, 4, int_values) - - self.writel(S_GEOM, 3, '</' + prim_type + '>') - - self.writel(S_GEOM, 2, '</mesh>') - self.writel(S_GEOM, 1, '</geometry>') - - meshdata = {} - meshdata["id"] = meshid - meshdata["material_assign"] = mat_assign - if (skeyindex == -1): - self.mesh_cache[node.data] = meshdata - - # Export armature data (if armature exists) - - if (armature is not None and ( - skel_source is not None or skeyindex == -1)): - - contid = self.new_id("controller") - - self.writel(S_SKIN, 1, '<controller id="' + contid + '">') - if (skel_source is not None): - self.writel(S_SKIN, 2, '<skin source="#' + skel_source + '">') - else: - self.writel(S_SKIN, 2, '<skin source="#' + meshid + '">') - - self.writel(S_SKIN, 3, '<bind_shape_matrix>' + - strmtx(node.matrix_world) + '</bind_shape_matrix>') - # Joint Names - self.writel(S_SKIN, 3, '<source id="' + contid + '-joints">') - name_values = "" - for v in si["bone_names"]: - name_values += " " + v - - self.writel(S_SKIN, 4, '<Name_array id="' + contid + - '-joints-array" count="' + - str(len(si["bone_names"])) + '">' + name_values + - '</Name_array>') - self.writel(S_SKIN, 4, '<technique_common>') - self.writel(S_SKIN, 4, '<accessor source="#' + contid + - '-joints-array" count="' + str(len(si["bone_names"])) + - '" stride="1">') - self.writel(S_SKIN, 5, '<param name="JOINT" type="Name"/>') - self.writel(S_SKIN, 4, '</accessor>') - self.writel(S_SKIN, 4, '</technique_common>') - self.writel(S_SKIN, 3, '</source>') - # Pose Matrices! - self.writel(S_SKIN, 3, '<source id="' + contid + '-bind_poses">') - pose_values = "" - for v in si["bone_bind_poses"]: - pose_values += " " + strmtx(v) - - self.writel(S_SKIN, 4, '<float_array id="' + contid + - '-bind_poses-array" count="' + - str(len(si["bone_bind_poses"]) * 16) + '">' + - pose_values + '</float_array>') - self.writel(S_SKIN, 4, '<technique_common>') - self.writel(S_SKIN, 4, '<accessor source="#' + contid + - '-bind_poses-array" count="' + - str(len(si["bone_bind_poses"])) + '" stride="16">') - self.writel(S_SKIN, 5, '<param name="TRANSFORM" type="float4x4"/>') - self.writel(S_SKIN, 4, '</accessor>') - self.writel(S_SKIN, 4, '</technique_common>') - self.writel(S_SKIN, 3, '</source>') - # Skin Weights! - self.writel(S_SKIN, 3, '<source id="' + contid + '-skin_weights">') - skin_weights = "" - skin_weights_total = 0 - for v in vertices: - skin_weights_total += len(v.weights) - for w in v.weights: - skin_weights += " " + str(w) - - self.writel(S_SKIN, 4, '<float_array id="' + contid + - '-skin_weights-array" count="' + - str(skin_weights_total) + '">' + skin_weights + - '</float_array>') - self.writel(S_SKIN, 4, '<technique_common>') - self.writel(S_SKIN, 4, '<accessor source="#' + contid + - '-skin_weights-array" count="' + - str(skin_weights_total) + '" stride="1">') - self.writel(S_SKIN, 5, '<param name="WEIGHT" type="float"/>') - self.writel(S_SKIN, 4, '</accessor>') - self.writel(S_SKIN, 4, '</technique_common>') - self.writel(S_SKIN, 3, '</source>') - - self.writel(S_SKIN, 3, '<joints>') - self.writel( - S_SKIN, 4, '<input semantic="JOINT" source="#' + contid + - '-joints"/>') - self.writel( - S_SKIN, 4, '<input semantic="INV_BIND_MATRIX" source="#' + - contid + '-bind_poses"/>') - self.writel(S_SKIN, 3, '</joints>') - self.writel(S_SKIN, 3, '<vertex_weights count="' + - str(len(vertices)) + '">') - self.writel(S_SKIN, 4, '<input semantic="JOINT" source="#' + - contid + '-joints" offset="0"/>') - self.writel(S_SKIN, 4, '<input semantic="WEIGHT" source="#' + - contid + '-skin_weights" offset="1"/>') - vcounts = "" - vs = "" - vcount = 0 - for v in vertices: - vcounts += " " + str(len(v.weights)) - for b in v.bones: - vs += " " + str(b) - vs += " " + str(vcount) - vcount += 1 - self.writel(S_SKIN, 4, '<vcount>' + vcounts + '</vcount>') - self.writel(S_SKIN, 4, '<v>' + vs + '</v>') - self.writel(S_SKIN, 3, '</vertex_weights>') - - self.writel(S_SKIN, 2, '</skin>') - self.writel(S_SKIN, 1, '</controller>') - meshdata["skin_id"] = contid - - return meshdata - - def export_mesh_node(self, node, il): - if (node.data is None): - return - - armature = None - armcount = 0 - for n in node.modifiers: - if (n.type == "ARMATURE"): - armcount += 1 - - if (node.parent is not None): - if (node.parent.type == "ARMATURE"): - armature = node.parent - if (armcount > 1): - self.operator.report({'WARNING'}, 'Object "' + node.name + - '" refers to more than one armature! ' - 'This is unsupported.') - if (armcount == 0): - self.operator.report({'WARNING'}, 'Object "' + node.name + - '" is child of an armature, but has ' - 'no armature modifier.') - - if (armcount > 0 and not armature): - self.operator.report({'WARNING'}, 'Object "' + node.name + - '" has armature modifier, but is not a child ' - 'of an armature. This is unsupported.') - - if (node.data.shape_keys is not None): - sk = node.data.shape_keys - if (sk.animation_data): - # print("HAS ANIM") - # print("DRIVERS: "+str(len(sk.animation_data.drivers))) - for d in sk.animation_data.drivers: - if (d.driver): - for v in d.driver.variables: - for t in v.targets: - if (t.id is not None and - t.id.name in self.scene.objects): - # print("LINKING " + str(node) + " WITH " + - # str(t.id.name)) - self.armature_for_morph[ - node] = self.scene.objects[t.id.name] - - meshdata = self.export_mesh(node, armature) - close_controller = False - - if ("skin_id" in meshdata): - close_controller = True - self.writel(S_NODES, il, '<instance_controller url="#' + - meshdata["skin_id"] + '">') - for sn in self.skeleton_info[armature]["skeleton_nodes"]: - self.writel(S_NODES, il + 1, '<skeleton>#' + - sn + '</skeleton>') - elif ("morph_id" in meshdata): - self.writel(S_NODES, il, '<instance_controller url="#' + - meshdata["morph_id"] + '">') - close_controller = True - elif (armature is None): - self.writel(S_NODES, il, '<instance_geometry url="#' + - meshdata["id"] + '">') - - if (len(meshdata["material_assign"]) > 0): - - self.writel(S_NODES, il + 1, '<bind_material>') - self.writel(S_NODES, il + 2, '<technique_common>') - for m in meshdata["material_assign"]: - self.writel(S_NODES, il + 3, '<instance_material symbol="' + - m[1] + '" target="#' + m[0] + '"/>') - - self.writel(S_NODES, il + 2, '</technique_common>') - self.writel(S_NODES, il + 1, '</bind_material>') - - if (close_controller): - self.writel(S_NODES, il, '</instance_controller>') - else: - self.writel(S_NODES, il, '</instance_geometry>') - - def export_armature_bone(self, bone, il, si): - boneid = self.new_id("bone") - boneidx = si["bone_count"] - si["bone_count"] += 1 - bonesid = si["id"] + "-" + str(boneidx) - if (bone.name in self.used_bones): - if (self.config["use_anim_action_all"]): - self.operator.report({'WARNING'}, 'Bone name "' + bone.name + - '" used in more than one skeleton. ' - 'Actions might export wrong.') - else: - self.used_bones.append(bone.name) - - si["bone_index"][bone.name] = boneidx - si["bone_ids"][bone] = boneid - si["bone_names"].append(bonesid) - self.writel(S_NODES, il, '<node id="' + boneid + '" sid="' + - bonesid + '" name="' + bone.name + '" type="JOINT">') - il += 1 - xform = bone.matrix_local - si["bone_bind_poses"].append((si["armature_xform"] * xform).inverted()) - - if (bone.parent is not None): - xform = bone.parent.matrix_local.inverted() * xform - else: - si["skeleton_nodes"].append(boneid) - - self.writel(S_NODES, il, '<matrix sid="transform">' + - strmtx(xform) + '</matrix>') - for c in bone.children: - self.export_armature_bone(c, il, si) - il -= 1 - self.writel(S_NODES, il, '</node>') - - def export_armature_node(self, node, il): - if (node.data is None): - return - - self.skeletons.append(node) - - armature = node.data - self.skeleton_info[node] = { - "bone_count": 0, - "id": self.new_id("skelbones"), - "name": node.name, "bone_index": {}, - "bone_ids": {}, "bone_names": [], "bone_bind_poses": [], - "skeleton_nodes": [], - "armature_xform": node.matrix_world} - - for b in armature.bones: - if (b.parent is not None): - continue - self.export_armature_bone(b, il, self.skeleton_info[node]) - - if (node.pose): - for b in node.pose.bones: - for x in b.constraints: - if (x.type == 'ACTION'): - self.action_constraints.append(x.action) - - def export_camera_node(self, node, il): - if (node.data is None): - return - - camera = node.data - camid = self.new_id("camera") - self.writel(S_CAMS, 1, '<camera id="' + camid + - '" name="' + camera.name + '">') - self.writel(S_CAMS, 2, '<optics>') - self.writel(S_CAMS, 3, '<technique_common>') - if (camera.type == "PERSP"): - self.writel(S_CAMS, 4, '<perspective>') - self.writel(S_CAMS, 5, '<yfov> ' + - # I think? - str(math.degrees(camera.angle)) + ' </yfov>') - self.writel(S_CAMS, 5, '<aspect_ratio> ' + - str(self.scene.render.resolution_x / - self.scene.render.resolution_y) + - ' </aspect_ratio>') - self.writel(S_CAMS, 5, '<znear> ' + - str(camera.clip_start) + ' </znear>') - self.writel(S_CAMS, 5, '<zfar> ' + - str(camera.clip_end) + ' </zfar>') - self.writel(S_CAMS, 4, '</perspective>') - else: - self.writel(S_CAMS, 4, '<orthographic>') - self.writel(S_CAMS, 5, '<xmag> ' + - str(camera.ortho_scale * 0.5) + ' </xmag>') # I think? - self.writel(S_CAMS, 5, '<aspect_ratio> ' + str( - self.scene.render.resolution_x / - self.scene.render.resolution_y) + ' </aspect_ratio>') - self.writel(S_CAMS, 5, '<znear> ' + - str(camera.clip_start) + ' </znear>') - self.writel(S_CAMS, 5, '<zfar> ' + - str(camera.clip_end) + ' </zfar>') - self.writel(S_CAMS, 4, '</orthographic>') - - self.writel(S_CAMS, 3, '</technique_common>') - self.writel(S_CAMS, 2, '</optics>') - self.writel(S_CAMS, 1, '</camera>') - - self.writel(S_NODES, il, '<instance_camera url="#' + camid + '"/>') - - def export_lamp_node(self, node, il): - if (node.data is None): - return - - light = node.data - lightid = self.new_id("light") - self.writel(S_LAMPS, 1, '<light id="' + lightid + - '" name="' + light.name + '">') - # self.writel(S_LAMPS, 2, '<optics>') - self.writel(S_LAMPS, 3, '<technique_common>') - - if (light.type == "POINT"): - self.writel(S_LAMPS, 4, '<point>') - self.writel(S_LAMPS, 5, '<color>' + - strarr(light.color) + '</color>') - # convert to linear attenuation - att_by_distance = 2.0 / light.distance - self.writel(S_LAMPS, 5, '<linear_attenuation>' + - str(att_by_distance) + '</linear_attenuation>') - if (light.use_sphere): - self.writel(S_LAMPS, 5, '<zfar>' + - str(light.distance) + '</zfar>') - - self.writel(S_LAMPS, 4, '</point>') - elif (light.type == "SPOT"): - self.writel(S_LAMPS, 4, '<spot>') - self.writel(S_LAMPS, 5, '<color>' + - strarr(light.color) + '</color>') - # convert to linear attenuation - att_by_distance = 2.0 / light.distance - self.writel(S_LAMPS, 5, '<linear_attenuation>' + - str(att_by_distance) + '</linear_attenuation>') - self.writel(S_LAMPS, 5, '<falloff_angle>' + - str(math.degrees(light.spot_size / 2)) + - '</falloff_angle>') - self.writel(S_LAMPS, 4, '</spot>') - - else: # write a sun lamp for everything else (not supported) - self.writel(S_LAMPS, 4, '<directional>') - self.writel(S_LAMPS, 5, '<color>' + - strarr(light.color) + '</color>') - self.writel(S_LAMPS, 4, '</directional>') - - self.writel(S_LAMPS, 3, '</technique_common>') - # self.writel(S_LAMPS, 2, '</optics>') - self.writel(S_LAMPS, 1, '</light>') - - self.writel(S_NODES, il, '<instance_light url="#' + lightid + '"/>') - - def export_empty_node(self, node, il): - self.writel(S_NODES, 4, '<extra>') - self.writel(S_NODES, 5, '<technique profile="GODOT">') - self.writel(S_NODES, 6, '<empty_draw_type>' + - node.empty_draw_type + '</empty_draw_type>') - self.writel(S_NODES, 5, '</technique>') - self.writel(S_NODES, 4, '</extra>') - - def export_curve(self, curve): - splineid = self.new_id("spline") - - self.writel(S_GEOM, 1, '<geometry id="' + - splineid + '" name="' + curve.name + '">') - self.writel(S_GEOM, 2, '<spline closed="0">') - - points = [] - interps = [] - handles_in = [] - handles_out = [] - tilts = [] - - for cs in curve.splines: - - if (cs.type == "BEZIER"): - for s in cs.bezier_points: - points.append(s.co[0]) - points.append(s.co[1]) - points.append(s.co[2]) - - handles_in.append(s.handle_left[0]) - handles_in.append(s.handle_left[1]) - handles_in.append(s.handle_left[2]) - - handles_out.append(s.handle_right[0]) - handles_out.append(s.handle_right[1]) - handles_out.append(s.handle_right[2]) - - tilts.append(s.tilt) - interps.append("BEZIER") - else: - - for s in cs.points: - points.append(s.co[0]) - points.append(s.co[1]) - points.append(s.co[2]) - handles_in.append(s.co[0]) - handles_in.append(s.co[1]) - handles_in.append(s.co[2]) - handles_out.append(s.co[0]) - handles_out.append(s.co[1]) - handles_out.append(s.co[2]) - tilts.append(s.tilt) - interps.append("LINEAR") - - self.writel(S_GEOM, 3, '<source id="' + splineid + '-positions">') - position_values = "" - for x in points: - position_values += " " + str(x) - self.writel(S_GEOM, 4, '<float_array id="' + splineid + - '-positions-array" count="' + - str(len(points)) + '">' + position_values + - '</float_array>') - self.writel(S_GEOM, 4, '<technique_common>') - self.writel(S_GEOM, 4, '<accessor source="#' + splineid + - '-positions-array" count="' + str(len(points) / 3) + - '" stride="3">') - self.writel(S_GEOM, 5, '<param name="X" type="float"/>') - self.writel(S_GEOM, 5, '<param name="Y" type="float"/>') - self.writel(S_GEOM, 5, '<param name="Z" type="float"/>') - self.writel(S_GEOM, 4, '</accessor>') - self.writel(S_GEOM, 3, '</source>') - - self.writel(S_GEOM, 3, '<source id="' + splineid + '-intangents">') - intangent_values = "" - for x in handles_in: - intangent_values += " " + str(x) - self.writel(S_GEOM, 4, '<float_array id="' + splineid + - '-intangents-array" count="' + - str(len(points)) + '">' + intangent_values + - '</float_array>') - self.writel(S_GEOM, 4, '<technique_common>') - self.writel(S_GEOM, 4, '<accessor source="#' + splineid + - '-intangents-array" count="' + str(len(points) / 3) + - '" stride="3">') - self.writel(S_GEOM, 5, '<param name="X" type="float"/>') - self.writel(S_GEOM, 5, '<param name="Y" type="float"/>') - self.writel(S_GEOM, 5, '<param name="Z" type="float"/>') - self.writel(S_GEOM, 4, '</accessor>') - self.writel(S_GEOM, 3, '</source>') - - self.writel(S_GEOM, 3, '<source id="' + splineid + '-outtangents">') - outtangent_values = "" - for x in handles_out: - outtangent_values += " " + str(x) - self.writel(S_GEOM, 4, '<float_array id="' + splineid + - '-outtangents-array" count="' + - str(len(points)) + '">' + outtangent_values + - '</float_array>') - self.writel(S_GEOM, 4, '<technique_common>') - self.writel(S_GEOM, 4, '<accessor source="#' + splineid + - '-outtangents-array" count="' + str(len(points) / 3) + - '" stride="3">') - self.writel(S_GEOM, 5, '<param name="X" type="float"/>') - self.writel(S_GEOM, 5, '<param name="Y" type="float"/>') - self.writel(S_GEOM, 5, '<param name="Z" type="float"/>') - self.writel(S_GEOM, 4, '</accessor>') - self.writel(S_GEOM, 3, '</source>') - - self.writel(S_GEOM, 3, '<source id="' + splineid + '-interpolations">') - interpolation_values = "" - for x in interps: - interpolation_values += " " + x - self.writel(S_GEOM, 4, '<Name_array id="' + splineid + - '-interpolations-array" count="' + - str(len(interps)) + '">' + interpolation_values + - '</Name_array>') - self.writel(S_GEOM, 4, '<technique_common>') - self.writel(S_GEOM, 4, '<accessor source="#' + splineid + - '-interpolations-array" count="' + str(len(interps)) + - '" stride="1">') - self.writel(S_GEOM, 5, '<param name="INTERPOLATION" type="name"/>') - self.writel(S_GEOM, 4, '</accessor>') - self.writel(S_GEOM, 3, '</source>') - - self.writel(S_GEOM, 3, '<source id="' + splineid + '-tilts">') - tilt_values = "" - for x in tilts: - tilt_values += " " + str(x) - self.writel(S_GEOM, 4, '<float_array id="' + splineid + - '-tilts-array" count="' + - str(len(tilts)) + '">' + tilt_values + '</float_array>') - self.writel(S_GEOM, 4, '<technique_common>') - self.writel(S_GEOM, 4, '<accessor source="#' + splineid + - '-tilts-array" count="' + str(len(tilts)) + - '" stride="1">') - self.writel(S_GEOM, 5, '<param name="TILT" type="float"/>') - self.writel(S_GEOM, 4, '</accessor>') - self.writel(S_GEOM, 3, '</source>') - - self.writel(S_GEOM, 3, '<control_vertices>') - self.writel( - S_GEOM, 4, '<input semantic="POSITION" source="#' + splineid + - '-positions"/>') - self.writel(S_GEOM, 4, '<input semantic="IN_TANGENT" source="#' + - splineid + '-intangents"/>') - self.writel(S_GEOM, 4, '<input semantic="OUT_TANGENT" source="#' + - splineid + '-outtangents"/>') - self.writel(S_GEOM, 4, '<input semantic="INTERPOLATION" source="#' + - splineid + '-interpolations"/>') - self.writel(S_GEOM, 4, '<input semantic="TILT" source="#' + - splineid + '-tilts"/>') - self.writel(S_GEOM, 3, '</control_vertices>') - - self.writel(S_GEOM, 2, '</spline>') - self.writel(S_GEOM, 1, '</geometry>') - - return splineid - - def export_curve_node(self, node, il): - if (node.data is None): - return - - curveid = self.export_curve(node.data) - - self.writel(S_NODES, il, '<instance_geometry url="#' + curveid + '">') - self.writel(S_NODES, il, '</instance_geometry>') - - def export_node(self, node, il): - if (node not in self.valid_nodes): - return - - prev_node = bpy.context.scene.objects.active - bpy.context.scene.objects.active = node - - self.writel(S_NODES, il, '<node id="' + self.validate_id(node.name) + - '" name="' + node.name + '" type="NODE">') - il += 1 - - self.writel(S_NODES, il, '<matrix sid="transform">' + - strmtx(node.matrix_local) + '</matrix>') - # print("NODE TYPE: "+node.type+" NAME: "+node.name) - if (node.type == "MESH"): - self.export_mesh_node(node, il) - elif (node.type == "CURVE"): - self.export_curve_node(node, il) - elif (node.type == "ARMATURE"): - self.export_armature_node(node, il) - elif (node.type == "CAMERA"): - self.export_camera_node(node, il) - elif (node.type == "LAMP"): - self.export_lamp_node(node, il) - elif (node.type == "EMPTY"): - self.export_empty_node(node, il) - - for x in node.children: - self.export_node(x, il) - - il -= 1 - self.writel(S_NODES, il, '</node>') - # make previous node active again - bpy.context.scene.objects.active = prev_node - - def is_node_valid(self, node): - if (node.type not in self.config["object_types"]): - return False - - if (self.config["use_active_layers"]): - valid = False - # print("NAME: "+node.name) - for i in range(20): - if (node.layers[i] and self.scene.layers[i]): - valid = True - break - if (not valid): - return False - - if (self.config["use_export_selected"] and not node.select): - return False - - return True - - def export_scene(self): - self.writel(S_NODES, 0, '<library_visual_scenes>') - self.writel(S_NODES, 1, '<visual_scene id="' + - self.scene_name + '" name="scene">') - - # validate nodes - for obj in self.scene.objects: - if (obj in self.valid_nodes): - continue - if (self.is_node_valid(obj)): - n = obj - while (n is not None): - if (n not in self.valid_nodes): - self.valid_nodes.append(n) - n = n.parent - - for obj in self.scene.objects: - if (obj in self.valid_nodes and obj.parent is None): - self.export_node(obj, 2) - - self.writel(S_NODES, 1, '</visual_scene>') - self.writel(S_NODES, 0, '</library_visual_scenes>') - - def export_asset(self): - self.writel(S_ASSET, 0, '<asset>') - # Why is this time stuff mandatory?, no one could care less... - self.writel(S_ASSET, 1, '<contributor>') - # Who made Collada, the FBI ? - self.writel(S_ASSET, 2, '<author> Anonymous </author>') - # Who made Collada, the FBI ? - self.writel( - S_ASSET, 2, '<authoring_tool> Collada Exporter for Blender 2.6+, ' - 'by Juan Linietsky (juan@codenix.com) </authoring_tool>') - self.writel(S_ASSET, 1, '</contributor>') - self.writel(S_ASSET, 1, '<created>' + - time.strftime("%Y-%m-%dT%H:%M:%SZ ") + '</created>') - self.writel(S_ASSET, 1, '<modified>' + - time.strftime("%Y-%m-%dT%H:%M:%SZ") + '</modified>') - self.writel(S_ASSET, 1, '<unit meter="1.0" name="meter"/>') - self.writel(S_ASSET, 1, '<up_axis>Z_UP</up_axis>') - self.writel(S_ASSET, 0, '</asset>') - - def export_animation_transform_channel(self, target, keys, matrices=True): - frame_total = len(keys) - anim_id = self.new_id("anim") - self.writel(S_ANIM, 1, '<animation id="' + anim_id + '">') - source_frames = "" - source_transforms = "" - source_interps = "" - - for k in keys: - source_frames += " " + str(k[0]) - if (matrices): - source_transforms += " " + strmtx(k[1]) - else: - source_transforms += " " + str(k[1]) - - source_interps += " LINEAR" - - # Time Source - self.writel(S_ANIM, 2, '<source id="' + anim_id + '-input">') - self.writel(S_ANIM, 3, '<float_array id="' + anim_id + - '-input-array" count="' + - str(frame_total) + '">' + source_frames + '</float_array>') - self.writel(S_ANIM, 3, '<technique_common>') - self.writel(S_ANIM, 4, '<accessor source="#' + anim_id + - '-input-array" count="' + str(frame_total) + - '" stride="1">') - self.writel(S_ANIM, 5, '<param name="TIME" type="float"/>') - self.writel(S_ANIM, 4, '</accessor>') - self.writel(S_ANIM, 3, '</technique_common>') - self.writel(S_ANIM, 2, '</source>') - - if (matrices): - # Transform Source - self.writel(S_ANIM, 2, '<source id="' + - anim_id + '-transform-output">') - self.writel(S_ANIM, 3, '<float_array id="' + anim_id + - '-transform-output-array" count="' + - str(frame_total * 16) + '">' + source_transforms + - '</float_array>') - self.writel(S_ANIM, 3, '<technique_common>') - self.writel(S_ANIM, 4, '<accessor source="#' + anim_id + - '-transform-output-array" count="' + str(frame_total) + - '" stride="16">') - self.writel(S_ANIM, 5, '<param name="TRANSFORM" type="float4x4"/>') - self.writel(S_ANIM, 4, '</accessor>') - self.writel(S_ANIM, 3, '</technique_common>') - self.writel(S_ANIM, 2, '</source>') - else: - # Value Source - self.writel(S_ANIM, 2, '<source id="' + - anim_id + '-transform-output">') - self.writel(S_ANIM, 3, '<float_array id="' + anim_id + - '-transform-output-array" count="' + - str(frame_total) + '">' + source_transforms + - '</float_array>') - self.writel(S_ANIM, 3, '<technique_common>') - self.writel(S_ANIM, 4, '<accessor source="#' + anim_id + - '-transform-output-array" count="' + str(frame_total) + - '" stride="1">') - self.writel(S_ANIM, 5, '<param name="X" type="float"/>') - self.writel(S_ANIM, 4, '</accessor>') - self.writel(S_ANIM, 3, '</technique_common>') - self.writel(S_ANIM, 2, '</source>') - - # Interpolation Source - self.writel(S_ANIM, 2, '<source id="' + - anim_id + '-interpolation-output">') - self.writel(S_ANIM, 3, '<Name_array id="' + anim_id + - '-interpolation-output-array" count="' + - str(frame_total) + '">' + source_interps + '</Name_array>') - self.writel(S_ANIM, 3, '<technique_common>') - self.writel(S_ANIM, 4, '<accessor source="#' + anim_id + - '-interpolation-output-array" count="' + str(frame_total) + - '" stride="1">') - self.writel(S_ANIM, 5, '<param name="INTERPOLATION" type="Name"/>') - self.writel(S_ANIM, 4, '</accessor>') - self.writel(S_ANIM, 3, '</technique_common>') - self.writel(S_ANIM, 2, '</source>') - - self.writel(S_ANIM, 2, '<sampler id="' + anim_id + '-sampler">') - self.writel(S_ANIM, 3, '<input semantic="INPUT" source="#' + - anim_id + '-input"/>') - self.writel(S_ANIM, 3, '<input semantic="OUTPUT" source="#' + - anim_id + '-transform-output"/>') - self.writel(S_ANIM, 3, '<input semantic="INTERPOLATION" source="#' + - anim_id + '-interpolation-output"/>') - self.writel(S_ANIM, 2, '</sampler>') - if (matrices): - self.writel(S_ANIM, 2, '<channel source="#' + anim_id + - '-sampler" target="' + target + '/transform"/>') - else: - self.writel(S_ANIM, 2, '<channel source="#' + - anim_id + '-sampler" target="' + target + '"/>') - self.writel(S_ANIM, 1, '</animation>') - - return [anim_id] - - def export_animation(self, start, end, allowed=None): - # Blender -> Collada frames needs a little work - # Collada starts from 0, blender usually from 1 - # The last frame must be included also - - frame_orig = self.scene.frame_current - - frame_len = 1.0 / self.scene.render.fps - frame_total = end - start + 1 - frame_sub = 0 - if (start > 0): - frame_sub = start * frame_len - - tcn = [] - xform_cache = {} - blend_cache = {} - # Change frames first, export objects last - # This improves performance enormously - - # print("anim from: " + str(start) + " to " + str(end) + " allowed: " + - # str(allowed)) - for t in range(start, end + 1): - self.scene.frame_set(t) - key = t * frame_len - frame_sub -# print("Export Anim Frame "+str(t)+"/"+str(self.scene.frame_end+1)) - - for node in self.scene.objects: - - if (node not in self.valid_nodes): - continue - if (allowed is not None and not (node in allowed)): - if (node.type == "MESH" and node.data is not None and - (node in self.armature_for_morph) and ( - self.armature_for_morph[node] in allowed)): - # all good you pass with flying colors for morphs - # inside of action - pass - else: - # print("fail "+str((node in self.armature_for_morph))) - continue - if (node.type == "MESH" and node.data is not None and - node.data.shape_keys is not None and ( - node.data in self.mesh_cache) and len( - node.data.shape_keys.key_blocks)): - target = self.mesh_cache[node.data]["morph_id"] - for i in range(len(node.data.shape_keys.key_blocks)): - - if (i == 0): - continue - - name = target + "-morph-weights(" + str(i - 1) + ")" - if (not (name in blend_cache)): - blend_cache[name] = [] - - blend_cache[name].append( - (key, node.data.shape_keys.key_blocks[i].value)) - - if (node.type == "MESH" and node.parent and - node.parent.type == "ARMATURE"): - - # In Collada, nodes that have skin modifier must not export - # animation, animate the skin instead. - continue - - if (len(node.constraints) > 0 or - node.animation_data is not None): - # If the node has constraints, or animation data, then - # export a sampled animation track - name = self.validate_id(node.name) - if (not (name in xform_cache)): - xform_cache[name] = [] - - mtx = node.matrix_world.copy() - if (node.parent): - mtx = node.parent.matrix_world.inverted() * mtx - - xform_cache[name].append((key, mtx)) - - if (node.type == "ARMATURE"): - # All bones exported for now - - for bone in node.data.bones: - - bone_name = self.skeleton_info[node]["bone_ids"][bone] - - if (not (bone_name in xform_cache)): - # print("has bone: " + bone_name) - xform_cache[bone_name] = [] - - posebone = node.pose.bones[bone.name] - parent_posebone = None - - mtx = posebone.matrix.copy() - if (bone.parent): - parent_posebone = node.pose.bones[bone.parent.name] - parent_invisible = False - - for i in range(3): - if (parent_posebone.scale[i] == 0.0): - parent_invisible = True - - if (not parent_invisible): - mtx = parent_posebone.matrix.inverted() * mtx - - xform_cache[bone_name].append((key, mtx)) - - self.scene.frame_set(frame_orig) - - # export animation xml - for nid in xform_cache: - tcn += self.export_animation_transform_channel( - nid, xform_cache[nid], True) - for nid in blend_cache: - tcn += self.export_animation_transform_channel( - nid, blend_cache[nid], False) - - return tcn - - def export_animations(self): - tmp_mat = [] - for s in self.skeletons: - tmp_bone_mat = [] - for bone in s.pose.bones: - tmp_bone_mat.append(Matrix(bone.matrix_basis)) - bone.matrix_basis = Matrix() - tmp_mat.append([Matrix(s.matrix_local), tmp_bone_mat]) - - self.writel(S_ANIM, 0, '<library_animations>') - - if (self.config["use_anim_action_all"] and len(self.skeletons)): - - cached_actions = {} - - for s in self.skeletons: - if s.animation_data and s.animation_data.action: - cached_actions[s] = s.animation_data.action.name - - self.writel(S_ANIM_CLIPS, 0, '<library_animation_clips>') - - for x in bpy.data.actions[:]: - if x.users == 0 or x in self.action_constraints: - continue - if (self.config["use_anim_skip_noexp"] and - x.name.endswith("-noexp")): - continue - - bones = [] - # find bones used - for p in x.fcurves: - dp = str(p.data_path) - base = "pose.bones[\"" - if (dp.find(base) == 0): - dp = dp[len(base):] - if (dp.find('"') != -1): - dp = dp[:dp.find('"')] - if (dp not in bones): - bones.append(dp) - - allowed_skeletons = [] - for i, y in enumerate(self.skeletons): - if (y.animation_data): - for z in y.pose.bones: - if (z.bone.name in bones): - if (y not in allowed_skeletons): - allowed_skeletons.append(y) - y.animation_data.action = x - - y.matrix_local = tmp_mat[i][0] - for j, bone in enumerate(s.pose.bones): - bone.matrix_basis = Matrix() - - # print("allowed skeletons "+str(allowed_skeletons)) - - # print(str(x)) - - tcn = self.export_animation(int(x.frame_range[0]), int( - x.frame_range[1] + 0.5), allowed_skeletons) - framelen = (1.0 / self.scene.render.fps) - start = x.frame_range[0] * framelen - end = x.frame_range[1] * framelen - # print("Export anim: "+x.name) - self.writel( - S_ANIM_CLIPS, 1, '<animation_clip name="' + x.name + - '" start="' + str(start) + '" end="' + str(end) + '">') - for z in tcn: - self.writel(S_ANIM_CLIPS, 2, - '<instance_animation url="#' + z + '"/>') - self.writel(S_ANIM_CLIPS, 1, '</animation_clip>') - if (len(tcn) == 0): - self.operator.report( - {'WARNING'}, 'Animation clip "' + x.name + - '" contains no tracks.') - - self.writel(S_ANIM_CLIPS, 0, '</library_animation_clips>') - - for i, s in enumerate(self.skeletons): - if (s.animation_data is None): - continue - if s in cached_actions: - s.animation_data.action = bpy.data.actions[ - cached_actions[s]] - else: - s.animation_data.action = None - for j, bone in enumerate(s.pose.bones): - bone.matrix_basis = tmp_mat[i][1][j] - - else: - self.export_animation(self.scene.frame_start, self.scene.frame_end) - - self.writel(S_ANIM, 0, '</library_animations>') - - def export(self): - self.writel(S_GEOM, 0, '<library_geometries>') - self.writel(S_CONT, 0, '<library_controllers>') - self.writel(S_CAMS, 0, '<library_cameras>') - self.writel(S_LAMPS, 0, '<library_lights>') - self.writel(S_IMGS, 0, '<library_images>') - self.writel(S_MATS, 0, '<library_materials>') - self.writel(S_FX, 0, '<library_effects>') - - self.skeletons = [] - self.action_constraints = [] - self.export_asset() - self.export_scene() - - self.writel(S_GEOM, 0, '</library_geometries>') - - # morphs always go before skin controllers - if S_MORPH in self.sections: - for l in self.sections[S_MORPH]: - self.writel(S_CONT, 0, l) - del self.sections[S_MORPH] - - # morphs always go before skin controllers - if S_SKIN in self.sections: - for l in self.sections[S_SKIN]: - self.writel(S_CONT, 0, l) - del self.sections[S_SKIN] - - self.writel(S_CONT, 0, '</library_controllers>') - self.writel(S_CAMS, 0, '</library_cameras>') - self.writel(S_LAMPS, 0, '</library_lights>') - self.writel(S_IMGS, 0, '</library_images>') - self.writel(S_MATS, 0, '</library_materials>') - self.writel(S_FX, 0, '</library_effects>') - - if (self.config["use_anim"]): - self.export_animations() - - try: - f = open(self.path, "wb") - except: - return False - - f.write(bytes('<?xml version="1.0" encoding="utf-8"?>\n', "UTF-8")) - f.write(bytes( - '<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" ' - 'version="1.4.1">\n', "UTF-8")) - - s = [] - for x in self.sections.keys(): - s.append(x) - s.sort() - for x in s: - for l in self.sections[x]: - f.write(bytes(l + "\n", "UTF-8")) - - f.write(bytes('<scene>\n', "UTF-8")) - f.write(bytes('\t<instance_visual_scene url="#' + - self.scene_name + '" />\n', "UTF-8")) - f.write(bytes('</scene>\n', "UTF-8")) - f.write(bytes('</COLLADA>\n', "UTF-8")) - return True - - def __init__(self, path, kwargs, operator): - self.operator = operator - self.scene = bpy.context.scene - self.last_id = 0 - self.scene_name = self.new_id("scene") - self.sections = {} - self.path = path - self.mesh_cache = {} - self.curve_cache = {} - self.material_cache = {} - self.image_cache = {} - self.skeleton_info = {} - self.config = kwargs - self.valid_nodes = [] - self.armature_for_morph = {} - self.used_bones = [] - self.wrongvtx_report = False - - -def save(operator, context, filepath="", use_selection=False, **kwargs): - exp = DaeExporter(filepath, kwargs, operator) - exp.export() - - return {'FINISHED'} # so the script wont run after we have batch exported. |