diff options
32 files changed, 1237 insertions, 144 deletions
diff --git a/core/cowdata.h b/core/cowdata.h index 66e7d1c343..6a8f644d53 100644 --- a/core/cowdata.h +++ b/core/cowdata.h @@ -100,6 +100,7 @@ private: } void _unref(void *p_data); + void _ref(const CowData *p_from); void _ref(const CowData &p_from); void _copy_on_write(); @@ -301,6 +302,11 @@ Error CowData<T>::resize(int p_size) { } template <class T> +void CowData<T>::_ref(const CowData *p_from) { + _ref(*p_from); +} + +template <class T> void CowData<T>::_ref(const CowData &p_from) { if (_ptr == p_from._ptr) diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp index 614fbb771f..b8fd13d67c 100644 --- a/core/io/image_loader.cpp +++ b/core/io/image_loader.cpp @@ -185,5 +185,5 @@ bool ResourceFormatLoaderImage::handles_type(const String &p_type) const { String ResourceFormatLoaderImage::get_resource_type(const String &p_path) const { - return "Image"; + return p_path.get_extension().to_lower() == "image" ? "Image" : String(); } diff --git a/core/vector.h b/core/vector.h index 7e3da34be0..52e8758f9b 100644 --- a/core/vector.h +++ b/core/vector.h @@ -44,17 +44,16 @@ template <class T> class VectorWriteProxy { friend class Vector<T>; - Vector<T> &_parent; + CowData<T> *_parent; - _FORCE_INLINE_ VectorWriteProxy(Vector<T> &parent) : + _FORCE_INLINE_ VectorWriteProxy(CowData<T> *parent) : _parent(parent){}; - VectorWriteProxy(const VectorWriteProxy<T> &p_other); public: _FORCE_INLINE_ T &operator[](int p_index) { - CRASH_BAD_INDEX(p_index, _parent.size()); + CRASH_BAD_INDEX(p_index, _parent->size()); - return _parent.ptrw()[p_index]; + return _parent->ptrw()[p_index]; } }; @@ -62,39 +61,39 @@ template <class T> class Vector { friend class VectorWriteProxy<T>; - CowData<T> _cowdata; + CowData<T> *_cowdata; public: VectorWriteProxy<T> write; bool push_back(const T &p_elem); - void remove(int p_index) { _cowdata.remove(p_index); } + void remove(int p_index) { _cowdata->remove(p_index); } void erase(const T &p_val) { int idx = find(p_val); if (idx >= 0) remove(idx); }; void invert(); - _FORCE_INLINE_ T *ptrw() { return _cowdata.ptrw(); } - _FORCE_INLINE_ const T *ptr() const { return _cowdata.ptr(); } + _FORCE_INLINE_ T *ptrw() { return _cowdata->ptrw(); } + _FORCE_INLINE_ const T *ptr() const { return _cowdata->ptr(); } _FORCE_INLINE_ void clear() { resize(0); } - _FORCE_INLINE_ bool empty() const { return _cowdata.empty(); } + _FORCE_INLINE_ bool empty() const { return _cowdata->empty(); } - _FORCE_INLINE_ T get(int p_index) { return _cowdata.get(p_index); } - _FORCE_INLINE_ const T get(int p_index) const { return _cowdata.get(p_index); } - _FORCE_INLINE_ void set(int p_index, const T &p_elem) { _cowdata.set(p_index, p_elem); } - _FORCE_INLINE_ int size() const { return _cowdata.size(); } - Error resize(int p_size) { return _cowdata.resize(p_size); } - _FORCE_INLINE_ const T &operator[](int p_index) const { return _cowdata.get(p_index); } - Error insert(int p_pos, const T &p_val) { return _cowdata.insert(p_pos, p_val); } + _FORCE_INLINE_ T get(int p_index) { return _cowdata->get(p_index); } + _FORCE_INLINE_ const T get(int p_index) const { return _cowdata->get(p_index); } + _FORCE_INLINE_ void set(int p_index, const T &p_elem) { _cowdata->set(p_index, p_elem); } + _FORCE_INLINE_ int size() const { return _cowdata->size(); } + Error resize(int p_size) { return _cowdata->resize(p_size); } + _FORCE_INLINE_ const T &operator[](int p_index) const { return _cowdata->get(p_index); } + Error insert(int p_pos, const T &p_val) { return _cowdata->insert(p_pos, p_val); } void append_array(const Vector<T> &p_other); template <class C> void sort_custom() { - int len = _cowdata.size(); + int len = _cowdata->size(); if (len == 0) return; @@ -110,7 +109,7 @@ public: void ordered_insert(const T &p_val) { int i; - for (i = 0; i < _cowdata.size(); i++) { + for (i = 0; i < _cowdata->size(); i++) { if (p_val < operator[](i)) { break; @@ -136,13 +135,19 @@ public: } _FORCE_INLINE_ Vector() : - write(VectorWriteProxy<T>(*this)) {} + _cowdata(new CowData<T>()), + write(VectorWriteProxy<T>(_cowdata)) {} _FORCE_INLINE_ Vector(const Vector &p_from) : - write(VectorWriteProxy<T>(*this)) { _cowdata._ref(p_from._cowdata); } + _cowdata(new CowData<T>()), + write(VectorWriteProxy<T>(_cowdata)) { _cowdata->_ref(p_from._cowdata); } inline Vector &operator=(const Vector &p_from) { - _cowdata._ref(p_from._cowdata); + _cowdata->_ref(p_from._cowdata); return *this; } + + _FORCE_INLINE_ ~Vector() { + delete _cowdata; + } }; template <class T> diff --git a/doc/classes/AudioStream.xml b/doc/classes/AudioStream.xml index 15bbb1625c..753a506058 100644 --- a/doc/classes/AudioStream.xml +++ b/doc/classes/AudioStream.xml @@ -16,6 +16,7 @@ <return type="float"> </return> <description> + Returns the length of the audio stream in seconds. </description> </method> </methods> diff --git a/doc/classes/AudioStreamPlayer.xml b/doc/classes/AudioStreamPlayer.xml index d6e75f8377..26d0b1a83d 100644 --- a/doc/classes/AudioStreamPlayer.xml +++ b/doc/classes/AudioStreamPlayer.xml @@ -17,7 +17,7 @@ <return type="float"> </return> <description> - Returns the position in the [AudioStream]. + Returns the position in the [AudioStream] in seconds. </description> </method> <method name="play"> diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index 2990906f7c..73d60e49b7 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -68,7 +68,7 @@ <return type="Array"> </return> <description> - Returns an array of all cells containing a tile from the tileset (i.e. a tile index different from [code]-1[/code]). + Returns a [Vector2] array with the positions of all cells containing a tile from the tileset (i.e. a tile index different from [code]-1[/code]). </description> </method> <method name="get_used_cells_by_id" qualifiers="const"> diff --git a/drivers/gles2/shader_gles2.h b/drivers/gles2/shader_gles2.h index 0c53c4ba72..49e45eea9f 100644 --- a/drivers/gles2/shader_gles2.h +++ b/drivers/gles2/shader_gles2.h @@ -300,7 +300,7 @@ public: case ShaderLanguage::TYPE_MAT3: { GLfloat mat[9]; - for (int i = 0; i < 0; i++) { + for (int i = 0; i < 9; i++) { mat[i] = values[i].real; } @@ -311,7 +311,7 @@ public: case ShaderLanguage::TYPE_MAT4: { GLfloat mat[16]; - for (int i = 0; i < 0; i++) { + for (int i = 0; i < 16; i++) { mat[i] = values[i].real; } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 212ee33ffa..15e40bfba1 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -109,6 +109,7 @@ #include "editor/plugins/shader_graph_editor_plugin.h" #include "editor/plugins/skeleton_2d_editor_plugin.h" #include "editor/plugins/skeleton_editor_plugin.h" +#include "editor/plugins/skeleton_ik_editor_plugin.h" #include "editor/plugins/spatial_editor_plugin.h" #include "editor/plugins/sprite_editor_plugin.h" #include "editor/plugins/sprite_frames_editor_plugin.h" @@ -5596,6 +5597,7 @@ EditorNode::EditorNode() { add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor))); add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor))); add_editor_plugin(memnew(SkeletonEditorPlugin(this))); + add_editor_plugin(memnew(SkeletonIKEditorPlugin(this))); add_editor_plugin(memnew(PhysicalBonePlugin(this))); // FIXME: Disabled as (according to reduz) users were complaining that it gets in the way diff --git a/editor/plugins/skeleton_ik_editor_plugin.cpp b/editor/plugins/skeleton_ik_editor_plugin.cpp new file mode 100644 index 0000000000..2d343d3edd --- /dev/null +++ b/editor/plugins/skeleton_ik_editor_plugin.cpp @@ -0,0 +1,110 @@ +/*************************************************************************/ +/* skeleton_ik_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "skeleton_ik_editor_plugin.h" + +#include "scene/animation/skeleton_ik.h" + +void SkeletonIKEditorPlugin::_play() { + + if (!skeleton_ik) + return; + + if (!skeleton_ik->get_parent_skeleton()) + return; + + if (play_btn->is_pressed()) { + + initial_bone_poses.resize(skeleton_ik->get_parent_skeleton()->get_bone_count()); + for (int i = 0; i < skeleton_ik->get_parent_skeleton()->get_bone_count(); ++i) { + initial_bone_poses.write[i] = skeleton_ik->get_parent_skeleton()->get_bone_pose(i); + } + + skeleton_ik->start(); + } else { + skeleton_ik->stop(); + + if (initial_bone_poses.size() != skeleton_ik->get_parent_skeleton()->get_bone_count()) + return; + + for (int i = 0; i < skeleton_ik->get_parent_skeleton()->get_bone_count(); ++i) { + skeleton_ik->get_parent_skeleton()->set_bone_pose(i, initial_bone_poses[i]); + } + } +} + +void SkeletonIKEditorPlugin::edit(Object *p_object) { + + if (p_object != skeleton_ik) { + if (skeleton_ik) { + play_btn->set_pressed(false); + _play(); + } + } + + SkeletonIK *s = Object::cast_to<SkeletonIK>(p_object); + if (!s) + return; + + skeleton_ik = s; +} + +bool SkeletonIKEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("SkeletonIK"); +} + +void SkeletonIKEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) + play_btn->show(); + else + play_btn->hide(); +} + +void SkeletonIKEditorPlugin::_bind_methods() { + + ClassDB::bind_method("_play", &SkeletonIKEditorPlugin::_play); +} + +SkeletonIKEditorPlugin::SkeletonIKEditorPlugin(EditorNode *p_node) { + + editor = p_node; + play_btn = memnew(Button); + play_btn->set_icon(editor->get_gui_base()->get_icon("Play", "EditorIcons")); + play_btn->set_text(TTR("Play IK")); + play_btn->set_toggle_mode(true); + play_btn->hide(); + play_btn->connect("pressed", this, "_play"); + add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, play_btn); + skeleton_ik = NULL; +} + +SkeletonIKEditorPlugin::~SkeletonIKEditorPlugin() {} diff --git a/editor/plugins/skeleton_ik_editor_plugin.h b/editor/plugins/skeleton_ik_editor_plugin.h new file mode 100644 index 0000000000..e645bea39a --- /dev/null +++ b/editor/plugins/skeleton_ik_editor_plugin.h @@ -0,0 +1,65 @@ +/*************************************************************************/ +/* skeleton_ik_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SKELETON_IK_EDITOR_PLUGIN_H +#define SKELETON_IK_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" + +class SkeletonIK; + +class SkeletonIKEditorPlugin : public EditorPlugin { + + GDCLASS(SkeletonIKEditorPlugin, EditorPlugin); + + SkeletonIK *skeleton_ik; + + Button *play_btn; + EditorNode *editor; + Vector<Transform> initial_bone_poses; + + void _play(); + +protected: + static void _bind_methods(); + +public: + virtual String get_name() const { return "SkeletonIK"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + SkeletonIKEditorPlugin(EditorNode *p_node); + ~SkeletonIKEditorPlugin(); +}; + +#endif // SKELETON_IK_EDITOR_PLUGIN_H diff --git a/modules/bullet/soft_body_bullet.cpp b/modules/bullet/soft_body_bullet.cpp index 1686a6e87e..9fc7230f91 100644 --- a/modules/bullet/soft_body_bullet.cpp +++ b/modules/bullet/soft_body_bullet.cpp @@ -123,7 +123,7 @@ void SoftBodyBullet::update_visual_server(SoftBodyVisualServerHandler *p_visual_ void SoftBodyBullet::set_soft_mesh(const Ref<Mesh> &p_mesh) { - if (p_mesh.is_null() || !p_mesh->surface_is_softbody_friendly(0)) + if (p_mesh.is_null()) soft_mesh.unref(); else soft_mesh = p_mesh; diff --git a/modules/mono/glue/cs_files/GD.cs b/modules/mono/glue/cs_files/GD.cs index e2457ff98b..43de9156f2 100644 --- a/modules/mono/glue/cs_files/GD.cs +++ b/modules/mono/glue/cs_files/GD.cs @@ -64,6 +64,11 @@ namespace Godot return ResourceLoader.Load(path); } + public static T Load<T>(string path) where T : Godot.Resource + { + return (T) ResourceLoader.Load(path); + } + public static void Print(params object[] what) { NativeCalls.godot_icall_Godot_print(what); diff --git a/modules/mono/glue/cs_files/ResourceLoaderExtensions.cs b/modules/mono/glue/cs_files/ResourceLoaderExtensions.cs new file mode 100644 index 0000000000..ceecc589e6 --- /dev/null +++ b/modules/mono/glue/cs_files/ResourceLoaderExtensions.cs @@ -0,0 +1,10 @@ +namespace Godot +{ + public static partial class ResourceLoader + { + public static T Load<T>(string path) where T : Godot.Resource + { + return (T) Load(path); + } + } +} diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp index ad74f73d74..4e515cde28 100644 --- a/modules/mono/mono_gd/gd_mono_class.cpp +++ b/modules/mono/mono_gd/gd_mono_class.cpp @@ -40,9 +40,7 @@ String GDMonoClass::get_full_name(MonoClass *p_mono_class) { MonoReflectionType *type_obj = mono_type_get_object(mono_domain_get(), get_mono_type(p_mono_class)); MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoString *str = mono_object_to_string((MonoObject *)type_obj, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; + MonoString *str = GDMonoUtils::object_to_string((MonoObject *)type_obj, &exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); return GDMonoMarshal::mono_string_to_godot(str); diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index c8df1038ce..630bda8b4e 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -105,9 +105,7 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, } MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoObject *ret = mono_runtime_invoke_array(mono_method, p_object, params, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; + MonoObject *ret = GDMonoUtils::runtime_invoke_array(mono_method, p_object, params, &exc); if (exc) { ret = NULL; @@ -121,9 +119,7 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params, return ret; } else { MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - mono_runtime_invoke(mono_method, p_object, NULL, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; + GDMonoUtils::runtime_invoke(mono_method, p_object, NULL, &exc); if (exc) { if (r_exc) { @@ -144,9 +140,7 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, MonoException **r_exc) { MonoObject *GDMonoMethod::invoke_raw(MonoObject *p_object, void **p_params, MonoException **r_exc) { MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoObject *ret = mono_runtime_invoke(mono_method, p_object, p_params, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; + MonoObject *ret = GDMonoUtils::runtime_invoke(mono_method, p_object, p_params, &exc); if (exc) { ret = NULL; diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp index a1c710c26c..ce66e0c8db 100644 --- a/modules/mono/mono_gd/gd_mono_property.cpp +++ b/modules/mono/mono_gd/gd_mono_property.cpp @@ -139,15 +139,23 @@ bool GDMonoProperty::has_setter() { } void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) { - void *params[1] = { p_value }; - set_value(p_object, params, r_exc); + MonoMethod *prop_method = mono_property_get_set_method(mono_property); + MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1); + mono_array_set(params, MonoObject *, 0, p_value); + MonoException *exc = NULL; + GDMonoUtils::runtime_invoke_array(prop_method, p_object, params, &exc); + if (exc) { + if (r_exc) { + *r_exc = exc; + } else { + GDMonoUtils::set_pending_exception(exc); + } + } } void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoException **r_exc) { MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - mono_property_set_value(mono_property, p_object, p_params, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; + GDMonoUtils::property_set_value(mono_property, p_object, p_params, &exc); if (exc) { if (r_exc) { @@ -160,9 +168,7 @@ void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoExcept MonoObject *GDMonoProperty::get_value(MonoObject *p_object, MonoException **r_exc) { MonoException *exc = NULL; - GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoObject *ret = mono_property_get_value(mono_property, p_object, NULL, (MonoObject **)&exc); - GD_MONO_END_RUNTIME_INVOKE; + MonoObject *ret = GDMonoUtils::property_get_value(mono_property, p_object, NULL, &exc); if (exc) { ret = NULL; diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 21efd4064a..911d629956 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -414,7 +414,7 @@ MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) { void *args[1] = { &new_array }; MonoException *exc = NULL; - mono_runtime_invoke(m, mono_object, args, (MonoObject **)&exc); + GDMonoUtils::runtime_invoke(m, mono_object, args, &exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); return mono_object; @@ -444,7 +444,7 @@ MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class) void *args[1] = { &new_dict }; MonoException *exc = NULL; - mono_runtime_invoke(m, mono_object, args, (MonoObject **)&exc); + GDMonoUtils::runtime_invoke(m, mono_object, args, &exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); return mono_object; @@ -476,9 +476,7 @@ String get_exception_name_and_message(MonoException *p_exc) { res += ": "; MonoProperty *prop = mono_class_get_property_from_name(klass, "Message"); - GD_MONO_BEGIN_RUNTIME_INVOKE; - MonoString *msg = (MonoString *)mono_property_get_value(prop, (MonoObject *)p_exc, NULL, NULL); - GD_MONO_END_RUNTIME_INVOKE; + MonoString *msg = (MonoString *)property_get_value(prop, (MonoObject *)p_exc, NULL, NULL); res += GDMonoMarshal::mono_string_to_godot(msg); return res; @@ -489,9 +487,7 @@ void set_exception_message(MonoException *p_exc, String message) { MonoProperty *prop = mono_class_get_property_from_name(klass, "Message"); MonoString *msg = GDMonoMarshal::mono_string_from_godot(message); void *params[1] = { msg }; - GD_MONO_BEGIN_RUNTIME_INVOKE; - mono_property_set_value(prop, (MonoObject *)p_exc, params, NULL); - GD_MONO_END_RUNTIME_INVOKE; + property_set_value(prop, (MonoObject *)p_exc, params, NULL); } void debug_print_unhandled_exception(MonoException *p_exc) { @@ -592,4 +588,38 @@ void set_pending_exception(MonoException *p_exc) { _THREAD_LOCAL_(int) current_invoke_count = 0; +MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **p_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoObject *ret = mono_runtime_invoke(p_method, p_obj, p_params, (MonoObject **)&p_exc); + GD_MONO_END_RUNTIME_INVOKE; + return ret; +} + +MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **p_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoObject *ret = mono_runtime_invoke_array(p_method, p_obj, p_params, (MonoObject **)&p_exc); + GD_MONO_END_RUNTIME_INVOKE; + return ret; +} + +MonoString *object_to_string(MonoObject *p_obj, MonoException **p_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoString *ret = mono_object_to_string(p_obj, (MonoObject **)p_exc); + GD_MONO_END_RUNTIME_INVOKE; + return ret; +} + +void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + mono_property_set_value(p_prop, p_obj, p_params, (MonoObject **)p_exc); + GD_MONO_END_RUNTIME_INVOKE; +} + +MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc) { + GD_MONO_BEGIN_RUNTIME_INVOKE; + MonoObject *ret = mono_property_get_value(p_prop, p_obj, p_params, (MonoObject **)p_exc); + GD_MONO_END_RUNTIME_INVOKE; + return ret; +} + } // namespace GDMonoUtils diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index d6774ed41d..bf8860c85a 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -230,6 +230,14 @@ _FORCE_INLINE_ int &get_runtime_invoke_count_ref() { return current_invoke_count; } +MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **p_exc); +MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **p_exc); + +MonoString *object_to_string(MonoObject *p_obj, MonoException **p_exc); + +void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc); +MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **p_exc); + } // namespace GDMonoUtils #define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoMarshal::mono_string_to_godot((MonoString *)m_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(NULL))) diff --git a/modules/mono/utils/thread_local.cpp b/modules/mono/utils/thread_local.cpp index 248f24c855..ae9f130518 100644 --- a/modules/mono/utils/thread_local.cpp +++ b/modules/mono/utils/thread_local.cpp @@ -69,7 +69,7 @@ struct ThreadLocalStorage::Impl { #define _CALLBACK_FUNC_ #endif - Impl(void _CALLBACK_FUNC_ (*p_destr_callback_func)(void *)) { + Impl(void (_CALLBACK_FUNC_ *p_destr_callback_func)(void *)) { #ifdef WINDOWS_ENABLED dwFlsIndex = FlsAlloc(p_destr_callback_func); ERR_FAIL_COND(dwFlsIndex == FLS_OUT_OF_INDEXES); @@ -95,7 +95,7 @@ void ThreadLocalStorage::set_value(void *p_value) const { pimpl->set_value(p_value); } -void ThreadLocalStorage::alloc(void _CALLBACK_FUNC_ (*p_destr_callback)(void *)) { +void ThreadLocalStorage::alloc(void (_CALLBACK_FUNC_ *p_destr_callback)(void *)) { pimpl = memnew(ThreadLocalStorage::Impl(p_destr_callback)); } diff --git a/modules/mono/utils/thread_local.h b/modules/mono/utils/thread_local.h index d0c5df3c91..783e40dc01 100644 --- a/modules/mono/utils/thread_local.h +++ b/modules/mono/utils/thread_local.h @@ -76,7 +76,7 @@ struct ThreadLocalStorage { void *get_value() const; void set_value(void *p_value) const; - void alloc(void _CALLBACK_FUNC_ (*p_dest_callback)(void *)); + void alloc(void (_CALLBACK_FUNC_ *p_dest_callback)(void *)); void free(); private: diff --git a/platform_methods.py b/platform_methods.py index 0e0d93d0a6..4300216427 100644 --- a/platform_methods.py +++ b/platform_methods.py @@ -7,6 +7,11 @@ import subprocess # NOTE: The multiprocessing module is not compatible with SCons due to conflict on cPickle +if sys.version_info[0] < 3: + JSON_SERIALIZABLE_TYPES = (bool, int, long, float, basestring) +else: + JSON_SERIALIZABLE_TYPES = (bool, int, float, str) + def run_in_subprocess(builder_function): @@ -17,9 +22,9 @@ def run_in_subprocess(builder_function): target = [node.srcnode().abspath for node in target] source = [node.srcnode().abspath for node in source] - # Short circuit on non-Windows platforms - if os.name != 'nt': - return builder_function(target, source, None) + # Short circuit on non-Windows platforms, no need to run in subprocess + if sys.platform not in ('win32', 'cygwin'): + return builder_function(target, source, env) # Identify module module_name = builder_function.__module__ @@ -32,14 +37,25 @@ def run_in_subprocess(builder_function): subprocess_env = os.environ.copy() subprocess_env['PYTHONPATH'] = os.pathsep.join([os.getcwd()] + sys.path) + # Keep only JSON serializable environment items + filtered_env = dict( + (key, value) + for key, value in env.items() + if isinstance(value, JSON_SERIALIZABLE_TYPES) + ) + # Save parameters - args = (target, source, None) + args = (target, source, filtered_env) data = dict(fn=function_name, args=args) json_path = os.path.join(os.environ['TMP'], uuid.uuid4().hex + '.json') with open(json_path, 'wt') as json_file: json.dump(data, json_file, indent=2) + json_file_size = os.stat(json_path).st_size + + print('Executing builder function in subprocess: ' + 'module_path=%r, parameter_file=%r, parameter_file_size=%r, target=%r, source=%r' % ( + module_path, json_path, json_file_size, target, source)) try: - print('Executing builder function in subprocess: module_path=%r; data=%r' % (module_path, data)) exit_code = subprocess.call([sys.executable, module_path, json_path], env=subprocess_env) finally: try: diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h index 4143989671..80bf422c98 100644 --- a/scene/3d/physics_body.h +++ b/scene/3d/physics_body.h @@ -557,6 +557,7 @@ protected: private: static Skeleton *find_skeleton_parent(Node *p_parent); + void _fix_joint_offset(); void _reload_joint(); diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp index 4b6b59b2d3..f45cb02211 100644 --- a/scene/3d/skeleton.cpp +++ b/scene/3d/skeleton.cpp @@ -600,9 +600,12 @@ PhysicalBone *Skeleton::_get_physical_bone_parent(int p_bone) { void Skeleton::_rebuild_physical_bones_cache() { const int b_size = bones.size(); for (int i = 0; i < b_size; ++i) { - bones.write[i].cache_parent_physical_bone = _get_physical_bone_parent(i); - if (bones[i].physical_bone) - bones[i].physical_bone->_on_bone_parent_changed(); + PhysicalBone *parent_pb = _get_physical_bone_parent(i); + if (parent_pb != bones[i].physical_bone) { + bones.write[i].cache_parent_physical_bone = parent_pb; + if (bones[i].physical_bone) + bones[i].physical_bone->_on_bone_parent_changed(); + } } } @@ -740,6 +743,8 @@ void Skeleton::_bind_methods() { #endif // _3D_DISABLED + ClassDB::bind_method(D_METHOD("set_bone_ignore_animation", "bone", "ignore"), &Skeleton::set_bone_ignore_animation); + BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON); } diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h index 9672acb57a..a8413ebaf2 100644 --- a/scene/3d/skeleton.h +++ b/scene/3d/skeleton.h @@ -39,6 +39,8 @@ */ #ifndef _3D_DISABLED +typedef int BoneId; + class PhysicalBone; #endif // _3D_DISABLED @@ -96,7 +98,7 @@ class Skeleton : public Spatial { void _make_dirty(); bool dirty; - //bind helpers + // bind helpers Array _get_bound_child_nodes_to_bone(int p_bone) const { Array bound; diff --git a/scene/3d/soft_body.cpp b/scene/3d/soft_body.cpp index 8498dc34c0..980c348c9b 100644 --- a/scene/3d/soft_body.cpp +++ b/scene/3d/soft_body.cpp @@ -98,7 +98,7 @@ SoftBody::PinnedPoint::PinnedPoint(const PinnedPoint &obj_tocopy) { point_index = obj_tocopy.point_index; spatial_attachment_path = obj_tocopy.spatial_attachment_path; spatial_attachment = obj_tocopy.spatial_attachment; - vertex_offset_transform = obj_tocopy.vertex_offset_transform; + offset = obj_tocopy.offset; } void SoftBody::_update_pickable() { @@ -133,8 +133,8 @@ bool SoftBody::_get(const StringName &p_name, Variant &r_ret) const { if ("pinned_points" == which) { Array arr_ret; - const int pinned_points_indices_size = pinned_points_indices.size(); - PoolVector<PinnedPoint>::Read r = pinned_points_indices.read(); + const int pinned_points_indices_size = pinned_points.size(); + PoolVector<PinnedPoint>::Read r = pinned_points.read(); arr_ret.resize(pinned_points_indices_size); for (int i = 0; i < pinned_points_indices_size; ++i) { @@ -157,13 +157,14 @@ bool SoftBody::_get(const StringName &p_name, Variant &r_ret) const { void SoftBody::_get_property_list(List<PropertyInfo> *p_list) const { - const int pinned_points_indices_size = pinned_points_indices.size(); + const int pinned_points_indices_size = pinned_points.size(); p_list->push_back(PropertyInfo(Variant::POOL_INT_ARRAY, "pinned_points")); for (int i = 0; i < pinned_points_indices_size; ++i) { p_list->push_back(PropertyInfo(Variant::INT, "attachments/" + itos(i) + "/point_index")); p_list->push_back(PropertyInfo(Variant::NODE_PATH, "attachments/" + itos(i) + "/spatial_attachment_path")); + p_list->push_back(PropertyInfo(Variant::VECTOR3, "attachments/" + itos(i) + "/offset")); } } @@ -172,17 +173,17 @@ bool SoftBody::_set_property_pinned_points_indices(const Array &p_indices) { const int p_indices_size = p_indices.size(); { // Remove the pined points on physics server that will be removed by resize - PoolVector<PinnedPoint>::Read r = pinned_points_indices.read(); - if (p_indices_size < pinned_points_indices.size()) { - for (int i = pinned_points_indices.size() - 1; i >= p_indices_size; --i) { + PoolVector<PinnedPoint>::Read r = pinned_points.read(); + if (p_indices_size < pinned_points.size()) { + for (int i = pinned_points.size() - 1; i >= p_indices_size; --i) { pin_point(r[i].point_index, false); } } } - pinned_points_indices.resize(p_indices_size); + pinned_points.resize(p_indices_size); - PoolVector<PinnedPoint>::Write w = pinned_points_indices.write(); + PoolVector<PinnedPoint>::Write w = pinned_points.write(); int point_index; for (int i = 0; i < p_indices_size; ++i) { point_index = p_indices.get(i); @@ -197,13 +198,17 @@ bool SoftBody::_set_property_pinned_points_indices(const Array &p_indices) { } bool SoftBody::_set_property_pinned_points_attachment(int p_item, const String &p_what, const Variant &p_value) { - if (pinned_points_indices.size() <= p_item) { + if (pinned_points.size() <= p_item) { return false; } if ("spatial_attachment_path" == p_what) { - PoolVector<PinnedPoint>::Write w = pinned_points_indices.write(); + PoolVector<PinnedPoint>::Write w = pinned_points.write(); pin_point(w[p_item].point_index, true, p_value); + _make_cache_dirty(); + } else if ("offset" == p_what) { + PoolVector<PinnedPoint>::Write w = pinned_points.write(); + w[p_item].offset = p_value; } else { return false; } @@ -212,15 +217,17 @@ bool SoftBody::_set_property_pinned_points_attachment(int p_item, const String & } bool SoftBody::_get_property_pinned_points(int p_item, const String &p_what, Variant &r_ret) const { - if (pinned_points_indices.size() <= p_item) { + if (pinned_points.size() <= p_item) { return false; } - PoolVector<PinnedPoint>::Read r = pinned_points_indices.read(); + PoolVector<PinnedPoint>::Read r = pinned_points.read(); if ("point_index" == p_what) { r_ret = r[p_item].point_index; } else if ("spatial_attachment_path" == p_what) { r_ret = r[p_item].spatial_attachment_path; + } else if ("offset" == p_what) { + r_ret = r[p_item].offset; } else { return false; } @@ -229,6 +236,8 @@ bool SoftBody::_get_property_pinned_points(int p_item, const String &p_what, Var } void SoftBody::_changed_callback(Object *p_changed, const char *p_prop) { + update_physics_server(); + _reset_points_offsets(); #ifdef TOOLS_ENABLED if (p_changed == this) { update_configuration_warning(); @@ -240,12 +249,13 @@ void SoftBody::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_WORLD: { - if (Engine::get_singleton()->is_editor_hint()) + if (Engine::get_singleton()->is_editor_hint()) { + add_change_receptor(this); + } RID space = get_world()->get_space(); PhysicsServer::get_singleton()->soft_body_set_space(physics_rid, space); - PhysicsServer::get_singleton()->soft_body_set_transform(physics_rid, get_global_transform()); update_physics_server(); } break; case NOTIFICATION_READY: { @@ -255,20 +265,32 @@ void SoftBody::_notification(int p_what) { } break; case NOTIFICATION_TRANSFORM_CHANGED: { - if (!simulation_started) { - PhysicsServer::get_singleton()->soft_body_set_transform(physics_rid, get_global_transform()); - - _update_cache_pin_points_datas(); - // Submit bone attachment - const int pinned_points_indices_size = pinned_points_indices.size(); - PoolVector<PinnedPoint>::Read r = pinned_points_indices.read(); - for (int i = 0; i < pinned_points_indices_size; ++i) { - if (!r[i].spatial_attachment) { - // Use soft body position to update the point position - PhysicsServer::get_singleton()->soft_body_move_point(physics_rid, r[i].point_index, (get_global_transform() * r[i].vertex_offset_transform).origin); - } else { - PhysicsServer::get_singleton()->soft_body_move_point(physics_rid, r[i].point_index, (r[i].spatial_attachment->get_global_transform() * r[i].vertex_offset_transform).origin); - } + if (Engine::get_singleton()->is_editor_hint()) { + _reset_points_offsets(); + return; + } + + PhysicsServer::get_singleton()->soft_body_set_transform(physics_rid, get_global_transform()); + + set_notify_transform(false); + // Required to be top level with Transform at center of world in order to modify VisualServer only to support custom Transform + set_as_toplevel(true); + set_transform(Transform()); + set_notify_transform(true); + + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + + if (!simulation_started) + return; + + _update_cache_pin_points_datas(); + // Submit bone attachment + const int pinned_points_indices_size = pinned_points.size(); + PoolVector<PinnedPoint>::Read r = pinned_points.read(); + for (int i = 0; i < pinned_points_indices_size; ++i) { + if (r[i].spatial_attachment) { + PhysicsServer::get_singleton()->soft_body_move_point(physics_rid, r[i].point_index, r[i].spatial_attachment->get_global_transform().xform(r[i].offset)); } } } break; @@ -408,8 +430,15 @@ void SoftBody::_draw_soft_mesh() { void SoftBody::update_physics_server() { - if (Engine::get_singleton()->is_editor_hint()) + if (Engine::get_singleton()->is_editor_hint()) { + + if (get_mesh().is_valid()) + PhysicsServer::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh()); + else + PhysicsServer::get_singleton()->soft_body_set_mesh(physics_rid, NULL); + return; + } if (get_mesh().is_valid()) { @@ -430,6 +459,9 @@ void SoftBody::become_mesh_owner() { if (!mesh_owner) { mesh_owner = true; + Vector<Ref<Material> > copy_materials; + copy_materials.append_array(materials); + ERR_FAIL_COND(!mesh->get_surface_count()); // Get current mesh array and create new mesh array with necessary flag for softbody @@ -443,11 +475,10 @@ void SoftBody::become_mesh_owner() { Ref<ArrayMesh> soft_mesh; soft_mesh.instance(); soft_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, surface_arrays, surface_blend_arrays, surface_format); + soft_mesh->surface_set_material(0, mesh->surface_get_material(0)); set_mesh(soft_mesh); - Vector<Ref<Material> > copy_materials; - copy_materials.append_array(materials); for (int i = copy_materials.size() - 1; 0 <= i; --i) { set_surface_material(i, copy_materials[i]); } @@ -506,15 +537,15 @@ const NodePath &SoftBody::get_parent_collision_ignore() const { } void SoftBody::set_pinned_points_indices(PoolVector<SoftBody::PinnedPoint> p_pinned_points_indices) { - pinned_points_indices = p_pinned_points_indices; - PoolVector<PinnedPoint>::Read w = pinned_points_indices.read(); - for (int i = pinned_points_indices.size() - 1; 0 <= i; --i) { + pinned_points = p_pinned_points_indices; + PoolVector<PinnedPoint>::Read w = pinned_points.read(); + for (int i = pinned_points.size() - 1; 0 <= i; --i) { pin_point(p_pinned_points_indices[i].point_index, true); } } PoolVector<SoftBody::PinnedPoint> SoftBody::get_pinned_points_indices() { - return pinned_points_indices; + return pinned_points; } void SoftBody::add_collision_exception_with(Node *p_node) { @@ -651,6 +682,8 @@ SoftBody::SoftBody() : pinned_points_cache_dirty(true) { PhysicsServer::get_singleton()->body_attach_object_instance_id(physics_rid, get_instance_id()); + //set_notify_transform(true); + set_physics_process_internal(true); } SoftBody::~SoftBody() { @@ -658,36 +691,30 @@ SoftBody::~SoftBody() { void SoftBody::reset_softbody_pin() { PhysicsServer::get_singleton()->soft_body_remove_all_pinned_points(physics_rid); - PoolVector<PinnedPoint>::Read pps = pinned_points_indices.read(); - for (int i = pinned_points_indices.size() - 1; 0 < i; --i) { + PoolVector<PinnedPoint>::Read pps = pinned_points.read(); + for (int i = pinned_points.size() - 1; 0 < i; --i) { PhysicsServer::get_singleton()->soft_body_pin_point(physics_rid, pps[i].point_index, true); } } -void SoftBody::_update_cache_pin_points_datas() { - if (pinned_points_cache_dirty) { - pinned_points_cache_dirty = false; +void SoftBody::_make_cache_dirty() { + pinned_points_cache_dirty = true; +} - PoolVector<PinnedPoint>::Write w = pinned_points_indices.write(); - for (int i = pinned_points_indices.size() - 1; 0 <= i; --i) { +void SoftBody::_update_cache_pin_points_datas() { + if (!pinned_points_cache_dirty) + return; - if (!w[i].spatial_attachment_path.is_empty()) { - w[i].spatial_attachment = Object::cast_to<Spatial>(get_node(w[i].spatial_attachment_path)); - if (w[i].spatial_attachment) { + pinned_points_cache_dirty = false; - Transform point_global_transform(get_global_transform()); - point_global_transform.translate(PhysicsServer::get_singleton()->soft_body_get_point_offset(physics_rid, w[i].point_index)); + PoolVector<PinnedPoint>::Write w = pinned_points.write(); + for (int i = pinned_points.size() - 1; 0 <= i; --i) { - // Local transform relative to spatial attachment node - w[i].vertex_offset_transform = w[i].spatial_attachment->get_global_transform().affine_inverse() * point_global_transform; - continue; - } else { - ERR_PRINTS("The node with path: " + String(w[i].spatial_attachment_path) + " was not found or is not a spatial node."); - } - } - // Local transform relative to Soft body - w[i].vertex_offset_transform.origin = PhysicsServer::get_singleton()->soft_body_get_point_offset(physics_rid, w[i].point_index); - w[i].vertex_offset_transform.basis = Basis(); + if (!w[i].spatial_attachment_path.is_empty()) { + w[i].spatial_attachment = Object::cast_to<Spatial>(get_node(w[i].spatial_attachment_path)); + } + if (!w[i].spatial_attachment) { + ERR_PRINT("Spatial node not defined in the pinned point, Softbody undefined behaviour!"); } } } @@ -699,22 +726,54 @@ void SoftBody::_pin_point_on_physics_server(int p_point_index, bool pin) { void SoftBody::_add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path) { SoftBody::PinnedPoint *pinned_point; if (-1 == _get_pinned_point(p_point_index, pinned_point)) { + // Create new PinnedPoint pp; pp.point_index = p_point_index; pp.spatial_attachment_path = p_spatial_attachment_path; - pinned_points_indices.push_back(pp); + + if (!p_spatial_attachment_path.is_empty() && has_node(p_spatial_attachment_path)) { + pp.spatial_attachment = Object::cast_to<Spatial>(get_node(p_spatial_attachment_path)); + pp.offset = (pp.spatial_attachment->get_global_transform().affine_inverse() * get_global_transform()).xform(PhysicsServer::get_singleton()->soft_body_get_point_global_position(physics_rid, pp.point_index)); + } + + pinned_points.push_back(pp); + } else { - // Update + pinned_point->point_index = p_point_index; pinned_point->spatial_attachment_path = p_spatial_attachment_path; + + if (!p_spatial_attachment_path.is_empty() && has_node(p_spatial_attachment_path)) { + pinned_point->spatial_attachment = Object::cast_to<Spatial>(get_node(p_spatial_attachment_path)); + pinned_point->offset = (pinned_point->spatial_attachment->get_global_transform().affine_inverse() * get_global_transform()).xform(PhysicsServer::get_singleton()->soft_body_get_point_global_position(physics_rid, pinned_point->point_index)); + } + } +} + +void SoftBody::_reset_points_offsets() { + + if (!Engine::get_singleton()->is_editor_hint()) + return; + + PoolVector<PinnedPoint>::Read r = pinned_points.read(); + PoolVector<PinnedPoint>::Write w = pinned_points.write(); + for (int i = pinned_points.size() - 1; 0 <= i; --i) { + + if (!r[i].spatial_attachment) + w[i].spatial_attachment = Object::cast_to<Spatial>(get_node(r[i].spatial_attachment_path)); + + if (!r[i].spatial_attachment) + continue; + + w[i].offset = (r[i].spatial_attachment->get_global_transform().affine_inverse() * get_global_transform()).xform(PhysicsServer::get_singleton()->soft_body_get_point_global_position(physics_rid, r[i].point_index)); } } void SoftBody::_remove_pinned_point(int p_point_index) { const int id(_has_pinned_point(p_point_index)); if (-1 != id) { - pinned_points_indices.remove(id); + pinned_points.remove(id); } } @@ -724,14 +783,14 @@ int SoftBody::_get_pinned_point(int p_point_index, SoftBody::PinnedPoint *&r_poi r_point = NULL; return -1; } else { - r_point = const_cast<SoftBody::PinnedPoint *>(&pinned_points_indices.read()[id]); + r_point = const_cast<SoftBody::PinnedPoint *>(&pinned_points.read()[id]); return id; } } int SoftBody::_has_pinned_point(int p_point_index) const { - PoolVector<PinnedPoint>::Read r = pinned_points_indices.read(); - for (int i = pinned_points_indices.size() - 1; 0 <= i; --i) { + PoolVector<PinnedPoint>::Read r = pinned_points.read(); + for (int i = pinned_points.size() - 1; 0 <= i; --i) { if (p_point_index == r[i].point_index) { return i; } diff --git a/scene/3d/soft_body.h b/scene/3d/soft_body.h index f44f337698..cee32b9651 100644 --- a/scene/3d/soft_body.h +++ b/scene/3d/soft_body.h @@ -72,9 +72,7 @@ public: int point_index; NodePath spatial_attachment_path; Spatial *spatial_attachment; // Cache - /// This is the offset from the soft body to point or attachment to point - /// Depend if the spatial_attachment_node is NULL or not - Transform vertex_offset_transform; // Cache + Vector3 offset; PinnedPoint(); PinnedPoint(const PinnedPoint &obj_tocopy); @@ -89,7 +87,7 @@ private: uint32_t collision_mask; uint32_t collision_layer; NodePath parent_collision_ignore; - PoolVector<PinnedPoint> pinned_points_indices; + PoolVector<PinnedPoint> pinned_points; bool simulation_started; bool pinned_points_cache_dirty; @@ -186,9 +184,14 @@ public: private: void reset_softbody_pin(); + + void _make_cache_dirty(); void _update_cache_pin_points_datas(); + void _pin_point_on_physics_server(int p_point_index, bool pin); void _add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path); + void _reset_points_offsets(); + void _remove_pinned_point(int p_point_index); int _get_pinned_point(int p_point_index, PinnedPoint *&r_point) const; int _has_pinned_point(int p_point_index) const; diff --git a/scene/animation/skeleton_ik.cpp b/scene/animation/skeleton_ik.cpp new file mode 100644 index 0000000000..4991cedfab --- /dev/null +++ b/scene/animation/skeleton_ik.cpp @@ -0,0 +1,551 @@ +/*************************************************************************/ +/* skeleton_ik.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/** + * @author AndreaCatania + */ + +#include "skeleton_ik.h" + +FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::find_child(const BoneId p_bone_id) { + for (int i = childs.size() - 1; 0 <= i; --i) { + if (p_bone_id == childs[i].bone) { + return &childs.write[i]; + } + } + return NULL; +} + +FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::add_child(const BoneId p_bone_id) { + const int infant_child_id = childs.size(); + childs.resize(infant_child_id + 1); + childs.write[infant_child_id].bone = p_bone_id; + childs.write[infant_child_id].parent_item = this; + return &childs.write[infant_child_id]; +} + +/// Build a chain that starts from the root to tip +void FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain) { + + ERR_FAIL_COND(-1 == p_task->root_bone); + + Chain &chain(p_task->chain); + + chain.tips.resize(p_task->end_effectors.size()); + chain.chain_root.bone = p_task->root_bone; + chain.chain_root.initial_transform = p_task->skeleton->get_bone_global_pose(chain.chain_root.bone); + chain.chain_root.current_pos = chain.chain_root.initial_transform.origin; + chain.chain_root.pb = p_task->skeleton->get_physical_bone(chain.chain_root.bone); + chain.middle_chain_item = NULL; + + // Holds all IDs that are composing a single chain in reverse order + Vector<BoneId> chain_ids; + // This is used to know the chain size + int sub_chain_size; + // Resize only one time in order to fit all joints for performance reason + chain_ids.resize(p_task->skeleton->get_bone_count()); + + for (int x = p_task->end_effectors.size() - 1; 0 <= x; --x) { + + const EndEffector *ee(&p_task->end_effectors[x]); + ERR_FAIL_COND(p_task->root_bone >= ee->tip_bone); + ERR_FAIL_INDEX(ee->tip_bone, p_task->skeleton->get_bone_count()); + + sub_chain_size = 0; + // Picks all IDs that composing a single chain in reverse order (except the root) + BoneId chain_sub_tip(ee->tip_bone); + while (chain_sub_tip > p_task->root_bone) { + + chain_ids.write[sub_chain_size++] = chain_sub_tip; + chain_sub_tip = p_task->skeleton->get_bone_parent(chain_sub_tip); + } + + BoneId middle_chain_item_id = (((float)sub_chain_size) * 0.5); + + // Build chain by reading chain ids in reverse order + // For each chain item id will be created a ChainItem if doesn't exists + ChainItem *sub_chain(&chain.chain_root); + for (int i = sub_chain_size - 1; 0 <= i; --i) { + + ChainItem *child_ci(sub_chain->find_child(chain_ids[i])); + if (!child_ci) { + + child_ci = sub_chain->add_child(chain_ids[i]); + + child_ci->pb = p_task->skeleton->get_physical_bone(child_ci->bone); + + child_ci->initial_transform = p_task->skeleton->get_bone_global_pose(child_ci->bone); + child_ci->current_pos = child_ci->initial_transform.origin; + + if (child_ci->parent_item) { + child_ci->length = (child_ci->current_pos - child_ci->parent_item->current_pos).length(); + } + } + + sub_chain = child_ci; + + if (middle_chain_item_id == i) { + chain.middle_chain_item = child_ci; + } + } + + if (!middle_chain_item_id) + chain.middle_chain_item = NULL; + + // Initialize current tip + chain.tips.write[x].chain_item = sub_chain; + chain.tips.write[x].end_effector = ee; + + if (p_force_simple_chain) { + // NOTE: + // This is an "hack" that force to create only one tip per chain since the solver of multi tip (end effector) + // is not yet created. + // Remove this code when this is done + break; + } + } +} + +void FabrikInverseKinematic::update_chain(const Skeleton *p_sk, ChainItem *p_chain_item) { + + if (!p_chain_item) + return; + + p_chain_item->initial_transform = p_sk->get_bone_global_pose(p_chain_item->bone); + p_chain_item->current_pos = p_chain_item->initial_transform.origin; + + for (int i = p_chain_item->childs.size() - 1; 0 <= i; --i) { + update_chain(p_sk, &p_chain_item->childs.write[i]); + } +} + +void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet) { + + real_t distance_to_goal(1e4); + real_t previous_distance_to_goal(0); + int can_solve(p_task->max_iterations); + while (distance_to_goal > p_task->min_distance && Math::abs(previous_distance_to_goal - distance_to_goal) > 0.005 && can_solve) { + previous_distance_to_goal = distance_to_goal; + --can_solve; + + solve_simple_backwards(p_task->chain, p_solve_magnet); + solve_simple_forwards(p_task->chain, p_solve_magnet); + + distance_to_goal = (p_task->chain.tips[0].chain_item->current_pos - p_task->chain.tips[0].end_effector->goal_transform.origin).length(); + } +} + +void FabrikInverseKinematic::solve_simple_backwards(Chain &r_chain, bool p_solve_magnet) { + + if (p_solve_magnet && !r_chain.middle_chain_item) { + return; + } + + Vector3 goal; + ChainItem *sub_chain_tip; + if (p_solve_magnet) { + goal = r_chain.magnet_position; + sub_chain_tip = r_chain.middle_chain_item; + } else { + goal = r_chain.tips[0].end_effector->goal_transform.origin; + sub_chain_tip = r_chain.tips[0].chain_item; + } + + while (sub_chain_tip) { + sub_chain_tip->current_pos = goal; + + if (sub_chain_tip->parent_item) { + // Not yet in the chain root + // So calculate next goal location + + const Vector3 look_parent((sub_chain_tip->parent_item->current_pos - sub_chain_tip->current_pos).normalized()); + goal = sub_chain_tip->current_pos + (look_parent * sub_chain_tip->length); + + // [TODO] Constraints goes here + } + + sub_chain_tip = sub_chain_tip->parent_item; + } +} + +void FabrikInverseKinematic::solve_simple_forwards(Chain &r_chain, bool p_solve_magnet) { + + if (p_solve_magnet && !r_chain.middle_chain_item) { + return; + } + + ChainItem *sub_chain_root(&r_chain.chain_root); + Vector3 origin(r_chain.chain_root.initial_transform.origin); + + while (sub_chain_root) { // Reach the tip + sub_chain_root->current_pos = origin; + + if (!sub_chain_root->childs.empty()) { + + ChainItem &child(sub_chain_root->childs.write[0]); + + // Is not tip + // So calculate next origin location + + // Look child + sub_chain_root->current_ori = (child.current_pos - sub_chain_root->current_pos).normalized(); + origin = sub_chain_root->current_pos + (sub_chain_root->current_ori * child.length); + + // [TODO] Constraints goes here + + if (p_solve_magnet && sub_chain_root == r_chain.middle_chain_item) { + // In case of magnet solving this is the tip + sub_chain_root = NULL; + } else { + sub_chain_root = &child; + } + } else { + + // Is tip + sub_chain_root = NULL; + } + } +} + +FabrikInverseKinematic::Task *FabrikInverseKinematic::create_simple_task(Skeleton *p_sk, BoneId root_bone, BoneId tip_bone, const Transform &goal_transform) { + + FabrikInverseKinematic::EndEffector ee; + ee.tip_bone = tip_bone; + + Task *task(memnew(Task)); + task->skeleton = p_sk; + task->root_bone = root_bone; + task->end_effectors.push_back(ee); + task->goal_global_transform = goal_transform; + + build_chain(task); + + return task; +} + +void FabrikInverseKinematic::free_task(Task *p_task) { + if (p_task) + memdelete(p_task); +} + +void FabrikInverseKinematic::set_goal(Task *p_task, const Transform &p_goal) { + p_task->goal_global_transform = p_goal; +} + +void FabrikInverseKinematic::make_goal(Task *p_task, const Transform &p_inverse_transf, real_t blending_delta) { + + if (blending_delta >= 0.99f) { + // Update the end_effector (local transform) without blending + p_task->end_effectors.write[0].goal_transform = p_inverse_transf * p_task->goal_global_transform; + } else { + + // End effector in local transform + const Transform end_effector_pose(p_task->skeleton->get_bone_global_pose(p_task->end_effectors.write[0].tip_bone)); + + // Update the end_effector (local transform) by blending with current pose + p_task->end_effectors.write[0].goal_transform = end_effector_pose.interpolate_with(p_inverse_transf * p_task->goal_global_transform, blending_delta); + } +} + +void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool p_use_magnet, const Vector3 &p_magnet_position) { + + if (blending_delta <= 0.01f) { + return; // Skip solving + } + + make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse().scaled(p_task->skeleton->get_global_transform().get_basis().get_scale()), blending_delta); + + update_chain(p_task->skeleton, &p_task->chain.chain_root); + + if (p_use_magnet && p_task->chain.middle_chain_item) { + p_task->chain.magnet_position = p_task->chain.middle_chain_item->initial_transform.origin.linear_interpolate(p_magnet_position, blending_delta); + solve_simple(p_task, true); + } + solve_simple(p_task, false); + + // Assign new bone position. + ChainItem *ci(&p_task->chain.chain_root); + while (ci) { + Transform new_bone_pose(ci->initial_transform); + new_bone_pose.origin = ci->current_pos; + + if (!ci->childs.empty()) { + + /// Rotate basis + const Vector3 initial_ori((ci->childs[0].initial_transform.origin - ci->initial_transform.origin).normalized()); + const Vector3 rot_axis(initial_ori.cross(ci->current_ori).normalized()); + + if (rot_axis[0] != 0 && rot_axis[1] != 0 && rot_axis[2] != 0) { + const real_t rot_angle(Math::acos(CLAMP(initial_ori.dot(ci->current_ori), -1, 1))); + new_bone_pose.basis.rotate(rot_axis, rot_angle); + } + } else { + // Set target orientation to tip + new_bone_pose.basis = p_task->chain.tips[0].end_effector->goal_transform.basis; + } + + p_task->skeleton->set_bone_global_pose(ci->bone, new_bone_pose); + + if (!ci->childs.empty()) + ci = &ci->childs.write[0]; + else + ci = NULL; + } +} + +void SkeletonIK::_validate_property(PropertyInfo &property) const { + + if (property.name == "root_bone" || property.name == "tip_bone") { + + if (skeleton) { + + String names; + for (int i = 0; i < skeleton->get_bone_count(); i++) { + if (i > 0) + names += ","; + names += skeleton->get_bone_name(i); + } + + property.hint = PROPERTY_HINT_ENUM; + property.hint_string = names; + } else { + + property.hint = PROPERTY_HINT_NONE; + property.hint_string = ""; + } + } +} + +void SkeletonIK::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_root_bone", "root_bone"), &SkeletonIK::set_root_bone); + ClassDB::bind_method(D_METHOD("get_root_bone"), &SkeletonIK::get_root_bone); + + ClassDB::bind_method(D_METHOD("set_tip_bone", "tip_bone"), &SkeletonIK::set_tip_bone); + ClassDB::bind_method(D_METHOD("get_tip_bone"), &SkeletonIK::get_tip_bone); + + ClassDB::bind_method(D_METHOD("set_interpolation", "interpolation"), &SkeletonIK::set_interpolation); + ClassDB::bind_method(D_METHOD("get_interpolation"), &SkeletonIK::get_interpolation); + + ClassDB::bind_method(D_METHOD("set_target_transform", "target"), &SkeletonIK::set_target_transform); + ClassDB::bind_method(D_METHOD("get_target_transform"), &SkeletonIK::get_target_transform); + + ClassDB::bind_method(D_METHOD("set_target_node", "node"), &SkeletonIK::set_target_node); + ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonIK::get_target_node); + + ClassDB::bind_method(D_METHOD("set_use_magnet", "use"), &SkeletonIK::set_use_magnet); + ClassDB::bind_method(D_METHOD("is_using_magnet"), &SkeletonIK::is_using_magnet); + + ClassDB::bind_method(D_METHOD("set_magnet_position", "local_position"), &SkeletonIK::set_magnet_position); + ClassDB::bind_method(D_METHOD("get_magnet_position"), &SkeletonIK::get_magnet_position); + + ClassDB::bind_method(D_METHOD("get_parent_skeleton"), &SkeletonIK::get_parent_skeleton); + ClassDB::bind_method(D_METHOD("is_running"), &SkeletonIK::is_running); + + ClassDB::bind_method(D_METHOD("set_min_distance", "min_distance"), &SkeletonIK::set_min_distance); + ClassDB::bind_method(D_METHOD("get_min_distance"), &SkeletonIK::get_min_distance); + + ClassDB::bind_method(D_METHOD("set_max_iterations", "iterations"), &SkeletonIK::set_max_iterations); + ClassDB::bind_method(D_METHOD("get_max_iterations"), &SkeletonIK::get_max_iterations); + + ClassDB::bind_method(D_METHOD("start", "one_time"), &SkeletonIK::start, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("stop"), &SkeletonIK::stop); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "root_bone"), "set_root_bone", "get_root_bone"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "tip_bone"), "set_tip_bone", "get_tip_bone"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "interpolation", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_interpolation", "get_interpolation"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "target"), "set_target_transform", "get_target_transform"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_magnet"), "set_use_magnet", "is_using_magnet"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "magnet"), "set_magnet_position", "get_magnet_position"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_node"), "set_target_node", "get_target_node"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "min_distance"), "set_min_distance", "get_min_distance"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_iterations"), "set_max_iterations", "get_max_iterations"); +} + +void SkeletonIK::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + skeleton = Object::cast_to<Skeleton>(get_parent()); + reload_chain(); + } break; + case NOTIFICATION_INTERNAL_PROCESS: { + + if (target_node_override) + reload_goal(); + + _solve_chain(); + + } break; + case NOTIFICATION_EXIT_TREE: { + reload_chain(); + } break; + } +} + +SkeletonIK::SkeletonIK() : + Node(), + interpolation(1), + skeleton(NULL), + target_node_override(NULL), + use_magnet(false), + min_distance(0.01), + max_iterations(10), + task(NULL) { + + set_process_priority(1); +} + +SkeletonIK::~SkeletonIK() { + FabrikInverseKinematic::free_task(task); + task = NULL; +} + +void SkeletonIK::set_root_bone(const StringName &p_root_bone) { + root_bone = p_root_bone; + reload_chain(); +} + +StringName SkeletonIK::get_root_bone() const { + return root_bone; +} + +void SkeletonIK::set_tip_bone(const StringName &p_tip_bone) { + tip_bone = p_tip_bone; + reload_chain(); +} + +StringName SkeletonIK::get_tip_bone() const { + return tip_bone; +} + +void SkeletonIK::set_interpolation(real_t p_interpolation) { + interpolation = p_interpolation; +} + +real_t SkeletonIK::get_interpolation() const { + return interpolation; +} + +void SkeletonIK::set_target_transform(const Transform &p_target) { + target = p_target; + reload_goal(); +} + +const Transform &SkeletonIK::get_target_transform() const { + return target; +} + +void SkeletonIK::set_target_node(const NodePath &p_node) { + target_node_path_override = p_node; + target_node_override = NULL; + reload_goal(); +} + +NodePath SkeletonIK::get_target_node() { + return target_node_path_override; +} + +void SkeletonIK::set_use_magnet(bool p_use) { + use_magnet = p_use; +} + +bool SkeletonIK::is_using_magnet() const { + return use_magnet; +} + +void SkeletonIK::set_magnet_position(const Vector3 &p_local_position) { + magnet_position = p_local_position; +} + +const Vector3 &SkeletonIK::get_magnet_position() const { + return magnet_position; +} + +void SkeletonIK::set_min_distance(real_t p_min_distance) { + min_distance = p_min_distance; +} + +void SkeletonIK::set_max_iterations(int p_iterations) { + max_iterations = p_iterations; +} + +bool SkeletonIK::is_running() { + return is_processing_internal(); +} + +void SkeletonIK::start(bool p_one_time) { + if (p_one_time) { + set_process_internal(false); + _solve_chain(); + } else { + set_process_internal(true); + } +} + +void SkeletonIK::stop() { + set_process_internal(false); +} + +Transform SkeletonIK::_get_target_transform() { + + if (!target_node_override && !target_node_path_override.is_empty()) + target_node_override = Object::cast_to<Spatial>(get_node(target_node_path_override)); + + if (target_node_override) + return target_node_override->get_global_transform(); + else + return target; +} + +void SkeletonIK::reload_chain() { + + FabrikInverseKinematic::free_task(task); + task = NULL; + + if (!skeleton) + return; + + task = FabrikInverseKinematic::create_simple_task(skeleton, skeleton->find_bone(root_bone), skeleton->find_bone(tip_bone), _get_target_transform()); + task->max_iterations = max_iterations; + task->min_distance = min_distance; +} + +void SkeletonIK::reload_goal() { + if (!task) + return; + + FabrikInverseKinematic::set_goal(task, _get_target_transform()); +} + +void SkeletonIK::_solve_chain() { + if (!task) + return; + FabrikInverseKinematic::solve(task, interpolation, use_magnet, magnet_position); +} diff --git a/scene/animation/skeleton_ik.h b/scene/animation/skeleton_ik.h new file mode 100644 index 0000000000..366c599c01 --- /dev/null +++ b/scene/animation/skeleton_ik.h @@ -0,0 +1,212 @@ +/*************************************************************************/ +/* skeleton_ik.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SKELETON_IK_H +#define SKELETON_IK_H + +/** + * @author AndreaCatania + */ + +#include "core/math/transform.h" +#include "scene/3d/skeleton.h" + +class FabrikInverseKinematic { + + struct EndEffector { + BoneId tip_bone; + Transform goal_transform; + }; + + struct ChainItem { + + Vector<ChainItem> childs; + ChainItem *parent_item; + + // Bone info + BoneId bone; + PhysicalBone *pb; + + real_t length; + /// Positions relative to root bone + Transform initial_transform; + Vector3 current_pos; + // Direction from this bone to child + Vector3 current_ori; + + ChainItem() : + parent_item(NULL), + bone(-1), + pb(NULL), + length(0) {} + + ChainItem *find_child(const BoneId p_bone_id); + ChainItem *add_child(const BoneId p_bone_id); + }; + + struct ChainTip { + ChainItem *chain_item; + const EndEffector *end_effector; + + ChainTip() : + chain_item(NULL), + end_effector(NULL) {} + + ChainTip(ChainItem *p_chain_item, const EndEffector *p_end_effector) : + chain_item(p_chain_item), + end_effector(p_end_effector) {} + + ChainTip(const ChainTip &p_other_ct) : + chain_item(p_other_ct.chain_item), + end_effector(p_other_ct.end_effector) {} + }; + + struct Chain { + ChainItem chain_root; + ChainItem *middle_chain_item; + Vector<ChainTip> tips; + Vector3 magnet_position; + }; + +public: + struct Task : public RID_Data { + RID self; + Skeleton *skeleton; + + Chain chain; + + // Settings + real_t min_distance; + int max_iterations; + + // Bone data + BoneId root_bone; + Vector<EndEffector> end_effectors; + + Transform goal_global_transform; + + Task() : + skeleton(NULL), + min_distance(0.01), + max_iterations(10), + root_bone(-1) {} + }; + +private: + /// Init a chain that starts from the root to tip + static void build_chain(Task *p_task, bool p_force_simple_chain = true); + + static void update_chain(const Skeleton *p_sk, ChainItem *p_chain_item); + + static void solve_simple(Task *p_task, bool p_solve_magnet); + /// Special solvers that solve only chains with one end effector + static void solve_simple_backwards(Chain &r_chain, bool p_solve_magnet); + static void solve_simple_forwards(Chain &r_chain, bool p_solve_magnet); + +public: + static Task *create_simple_task(Skeleton *p_sk, BoneId root_bone, BoneId tip_bone, const Transform &goal_transform); + static void free_task(Task *p_task); + // The goal of chain should be always in local space + static void set_goal(Task *p_task, const Transform &p_goal); + static void make_goal(Task *p_task, const Transform &p_inverse_transf, real_t blending_delta); + static void solve(Task *p_task, real_t blending_delta, bool p_use_magnet, const Vector3 &p_magnet_position); +}; + +class SkeletonIK : public Node { + GDCLASS(SkeletonIK, Node); + + StringName root_bone; + StringName tip_bone; + real_t interpolation; + Transform target; + NodePath target_node_path_override; + bool use_magnet; + Vector3 magnet_position; + + real_t min_distance; + int max_iterations; + + Skeleton *skeleton; + Spatial *target_node_override; + FabrikInverseKinematic::Task *task; + +protected: + virtual void + _validate_property(PropertyInfo &property) const; + + static void _bind_methods(); + virtual void _notification(int p_notification); + +public: + SkeletonIK(); + virtual ~SkeletonIK(); + + void set_root_bone(const StringName &p_root_bone); + StringName get_root_bone() const; + + void set_tip_bone(const StringName &p_tip_bone); + StringName get_tip_bone() const; + + void set_interpolation(real_t p_interpolation); + real_t get_interpolation() const; + + void set_target_transform(const Transform &p_target); + const Transform &get_target_transform() const; + + void set_target_node(const NodePath &p_node); + NodePath get_target_node(); + + void set_use_magnet(bool p_use); + bool is_using_magnet() const; + + void set_magnet_position(const Vector3 &p_constraint); + const Vector3 &get_magnet_position() const; + + void set_min_distance(real_t p_min_distance); + real_t get_min_distance() const { return min_distance; } + + void set_max_iterations(int p_iterations); + int get_max_iterations() const { return max_iterations; } + + Skeleton *get_parent_skeleton() const { return skeleton; } + + bool is_running(); + + void start(bool p_one_time = false); + void stop(); + +private: + Transform _get_target_transform(); + void reload_chain(); + void reload_goal(); + void _solve_chain(); +}; + +#endif // SKELETON_IK_H diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 68de7b3a0a..6144240328 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1415,11 +1415,6 @@ bool Node::is_greater_than(const Node *p_node) const { return res; } -bool Node::has_priority_higher_than(const Node *p_node) const { - ERR_FAIL_NULL_V(p_node, false); - return data.process_priority > p_node->data.process_priority; -} - void Node::get_owned_by(Node *p_by, List<Node *> *p_owned) { if (data.owner == p_by) diff --git a/scene/main/node.h b/scene/main/node.h index 9ee0340678..f3422618ce 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -72,7 +72,7 @@ public: struct ComparatorWithPriority { - bool operator()(const Node *p_a, const Node *p_b) const { return p_b->has_priority_higher_than(p_a) || p_b->is_greater_than(p_a); } + bool operator()(const Node *p_a, const Node *p_b) const { return p_b->data.process_priority == p_a->data.process_priority ? p_b->is_greater_than(p_a) : p_b->data.process_priority > p_a->data.process_priority; } }; private: @@ -265,7 +265,6 @@ public: bool is_a_parent_of(const Node *p_node) const; bool is_greater_than(const Node *p_node) const; - bool has_priority_higher_than(const Node *p_node) const; NodePath get_path() const; NodePath get_path_to(const Node *p_node) const; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 753fcf2a13..f681d30ccc 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -204,6 +204,7 @@ #include "scene/3d/sprite_3d.h" #include "scene/3d/vehicle_body.h" #include "scene/3d/visibility_notifier.h" +#include "scene/animation/skeleton_ik.h" #include "scene/resources/environment.h" #include "scene/resources/physics_material.h" #endif @@ -361,6 +362,7 @@ void register_scene_types() { ClassDB::register_class<Spatial>(); ClassDB::register_virtual_class<SpatialGizmo>(); ClassDB::register_class<Skeleton>(); + ClassDB::register_class<SkeletonIK>(); ClassDB::register_class<AnimationPlayer>(); ClassDB::register_class<Tween>(); diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 446b2b0e68..07783d5f4a 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -395,7 +395,15 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map nd.name = _nm_get_string(p_node->get_name(), name_map); nd.instance = -1; //not instanced by default - nd.index = p_node->get_index(); + + //really convoluted condition, but it basically checks that index is only saved when part of an inherited scene OR the node parent is from the edited scene + if (p_owner->get_scene_inherited_state().is_null() && (p_node == p_owner || (p_node->get_owner() == p_owner && (p_node->get_parent() == p_owner || p_node->get_parent()->get_owner() == p_owner)))) { + //do not save index, because it belongs to saved scene and scene is not inherited + nd.index = -1; + } else { + //part of an inherited scene, or parent is from an instanced scene + nd.index = p_node->get_index(); + } // if this node is part of an instanced scene or sub-instanced scene // we need to get the corresponding instance states. |