summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore9
-rw-r--r--SConstruct2
-rw-r--r--core/bind/core_bind.cpp13
-rw-r--r--core/bind/core_bind.h2
-rw-r--r--core/os/input.cpp1
-rw-r--r--core/os/input.h1
-rw-r--r--core/variant_op.cpp24
-rw-r--r--doc/base/classes.xml37
-rw-r--r--main/input_default.cpp23
-rw-r--r--main/input_default.h3
-rwxr-xr-xmethods.py4
-rw-r--r--modules/gdscript/gd_parser.cpp4
-rw-r--r--platform/android/SCsub26
-rw-r--r--platform/android/build.gradle.template23
-rw-r--r--platform/android/detect.py2
-rw-r--r--platform/android/export/export.cpp2
-rw-r--r--platform/android/java/gradle/wrapper/gradle-wrapper.properties2
-rw-r--r--platform/windows/os_windows.cpp62
-rw-r--r--platform/windows/os_windows.h1
-rw-r--r--scene/2d/camera_2d.cpp31
-rw-r--r--scene/2d/camera_2d.h2
-rw-r--r--scene/2d/tile_map.cpp36
-rw-r--r--scene/2d/tile_map.h3
-rw-r--r--scene/animation/animation_tree_player.cpp170
-rw-r--r--scene/animation/animation_tree_player.h5
-rw-r--r--scene/gui/rich_text_label.cpp30
-rw-r--r--scene/gui/rich_text_label.h1
-rw-r--r--scene/resources/sample_library.cpp19
-rw-r--r--scene/resources/sample_library.h4
-rw-r--r--servers/visual/visual_server_raster.cpp12
-rw-r--r--servers/visual/visual_server_raster.h4
-rw-r--r--servers/visual/visual_server_wrap_mt.h2
-rw-r--r--servers/visual_server.h2
-rw-r--r--tools/editor/animation_editor.cpp357
-rw-r--r--tools/editor/animation_editor.h9
-rw-r--r--tools/editor/create_dialog.cpp14
-rw-r--r--tools/editor/editor_autoload_settings.cpp618
-rw-r--r--tools/editor/editor_autoload_settings.h94
-rw-r--r--tools/editor/editor_data.cpp38
-rw-r--r--tools/editor/editor_data.h7
-rw-r--r--tools/editor/editor_node.cpp58
-rw-r--r--tools/editor/editor_node.h5
-rw-r--r--tools/editor/editor_run_native.cpp4
-rw-r--r--tools/editor/plugins/spatial_editor_plugin.cpp11
-rw-r--r--tools/editor/plugins/tile_map_editor_plugin.cpp18
-rw-r--r--tools/editor/plugins/tile_set_editor_plugin.cpp22
-rw-r--r--tools/editor/project_settings.cpp429
-rw-r--r--tools/editor/project_settings.h19
-rw-r--r--tools/editor/property_editor.cpp2
-rw-r--r--tools/editor/scene_tree_dock.cpp4
-rw-r--r--tools/editor/spatial_editor_gizmos.cpp5
-rw-r--r--tools/export/blender25/godot_export_manager.py576
-rw-r--r--tools/export/blender25/install.txt11
-rw-r--r--tools/export/blender25/io_scene_dae/__init__.py192
-rw-r--r--tools/export/blender25/io_scene_dae/export_dae.py1923
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.