Browse Source

Merge pull request #32275 from godotengine/skin_support

Added skin support and simplified APIs to override bone position + glTF 2.0 import fixes
Rémi Verschelde 5 years ago
parent
commit
159470df08

+ 31 - 0
core/math/disjoint_set.cpp

@@ -0,0 +1,31 @@
+/*************************************************************************/
+/*  disjoint_set.h                                                       */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2019 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 "disjoint_set.h"

+ 155 - 0
core/math/disjoint_set.h

@@ -0,0 +1,155 @@
+/*************************************************************************/
+/*  disjoint_set.h                                                       */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2019 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 DISJOINT_SET_H
+#define DISJOINT_SET_H
+
+#include "core/map.h"
+#include "core/vector.h"
+
+/**
+	@author Marios Staikopoulos <[email protected]>
+*/
+
+/* This DisjointSet class uses Find with path compression and Union by rank */
+template <typename T, class C = Comparator<T>, class AL = DefaultAllocator>
+class DisjointSet {
+
+	struct Element {
+		T object;
+		Element *parent = nullptr;
+		int rank = 0;
+	};
+
+	typedef Map<T, Element *, C, AL> MapT;
+
+	MapT elements;
+
+	Element *get_parent(Element *element);
+
+	_FORCE_INLINE_ Element *insert_or_get(T object);
+
+public:
+	~DisjointSet();
+
+	_FORCE_INLINE_ void insert(T object) { (void)insert_or_get(object); }
+
+	void create_union(T a, T b);
+
+	void get_representatives(Vector<T> &out_roots);
+
+	void get_members(Vector<T> &out_members, T representative);
+};
+
+/* FUNCTIONS */
+
+template <typename T, class C, class AL>
+DisjointSet<T, C, AL>::~DisjointSet() {
+	for (typename MapT::Element *itr = elements.front(); itr != nullptr; itr = itr->next()) {
+		memdelete_allocator<Element, AL>(itr->value());
+	}
+}
+
+template <typename T, class C, class AL>
+typename DisjointSet<T, C, AL>::Element *DisjointSet<T, C, AL>::get_parent(Element *element) {
+	if (element->parent != element) {
+		element->parent = get_parent(element->parent);
+	}
+
+	return element->parent;
+}
+
+template <typename T, class C, class AL>
+typename DisjointSet<T, C, AL>::Element *DisjointSet<T, C, AL>::insert_or_get(T object) {
+	typename MapT::Element *itr = elements.find(object);
+	if (itr != nullptr) {
+		return itr->value();
+	}
+
+	Element *new_element = memnew_allocator(Element, AL);
+	new_element->object = object;
+	new_element->parent = new_element;
+	elements.insert(object, new_element);
+
+	return new_element;
+}
+
+template <typename T, class C, class AL>
+void DisjointSet<T, C, AL>::create_union(T a, T b) {
+
+	Element *x = insert_or_get(a);
+	Element *y = insert_or_get(b);
+
+	Element *x_root = get_parent(x);
+	Element *y_root = get_parent(y);
+
+	// Already in the same set
+	if (x_root == y_root)
+		return;
+
+	// Not in the same set, merge
+	if (x_root->rank < y_root->rank) {
+		SWAP(x_root, y_root);
+	}
+
+	// Merge y_root into x_root
+	y_root->parent = x_root;
+	if (x_root->rank == y_root->rank) {
+		++x_root->rank;
+	}
+}
+
+template <typename T, class C, class AL>
+void DisjointSet<T, C, AL>::get_representatives(Vector<T> &out_representatives) {
+	for (typename MapT::Element *itr = elements.front(); itr != nullptr; itr = itr->next()) {
+		Element *element = itr->value();
+		if (element->parent == element) {
+			out_representatives.push_back(element->object);
+		}
+	}
+}
+
+template <typename T, class C, class AL>
+void DisjointSet<T, C, AL>::get_members(Vector<T> &out_members, T representative) {
+	typename MapT::Element *rep_itr = elements.find(representative);
+	ERR_FAIL_COND(rep_itr == nullptr);
+
+	Element *rep_element = rep_itr->value();
+	ERR_FAIL_COND(rep_element->parent != rep_element);
+
+	for (typename MapT::Element *itr = elements.front(); itr != nullptr; itr = itr->next()) {
+		Element *parent = get_parent(itr->value());
+		if (parent == rep_element) {
+			out_members.push_back(itr->key());
+		}
+	}
+}
+
+#endif

+ 0 - 6
drivers/gles2/rasterizer_scene_gles2.cpp

@@ -2570,12 +2570,6 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
 
 
 		state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform);
 		state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform);
 
 
-		if (skeleton) {
-			state.scene_shader.set_uniform(SceneShaderGLES2::SKELETON_IN_WORLD_COORDS, skeleton->use_world_transform);
-			state.scene_shader.set_uniform(SceneShaderGLES2::SKELETON_TRANSFORM, skeleton->world_transform);
-			state.scene_shader.set_uniform(SceneShaderGLES2::SKELETON_TRANSFORM_INVERSE, skeleton->world_transform_inverse);
-		}
-
 		if (use_lightmap_capture) { //this is per instance, must be set always if present
 		if (use_lightmap_capture) { //this is per instance, must be set always if present
 			glUniform4fv(state.scene_shader.get_uniform_location(SceneShaderGLES2::LIGHTMAP_CAPTURES), 12, (const GLfloat *)e->instance->lightmap_capture_data.ptr());
 			glUniform4fv(state.scene_shader.get_uniform_location(SceneShaderGLES2::LIGHTMAP_CAPTURES), 12, (const GLfloat *)e->instance->lightmap_capture_data.ptr());
 			state.scene_shader.set_uniform(SceneShaderGLES2::LIGHTMAP_CAPTURE_SKY, false);
 			state.scene_shader.set_uniform(SceneShaderGLES2::LIGHTMAP_CAPTURE_SKY, false);

+ 0 - 17
drivers/gles2/rasterizer_storage_gles2.cpp

@@ -3663,23 +3663,6 @@ void RasterizerStorageGLES2::skeleton_set_base_transform_2d(RID p_skeleton, cons
 	skeleton->base_transform_2d = p_base_transform;
 	skeleton->base_transform_2d = p_base_transform;
 }
 }
 
 
-void RasterizerStorageGLES2::skeleton_set_world_transform(RID p_skeleton, bool p_enable, const Transform &p_world_transform) {
-
-	Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
-
-	ERR_FAIL_COND(skeleton->use_2d);
-
-	skeleton->world_transform = p_world_transform;
-	skeleton->use_world_transform = p_enable;
-	if (p_enable) {
-		skeleton->world_transform_inverse = skeleton->world_transform.affine_inverse();
-	}
-
-	if (!skeleton->update_list.in_list()) {
-		skeleton_update_list.add(&skeleton->update_list);
-	}
-}
-
 void RasterizerStorageGLES2::_update_skeleton_transform_buffer(const PoolVector<float> &p_data, size_t p_size) {
 void RasterizerStorageGLES2::_update_skeleton_transform_buffer(const PoolVector<float> &p_data, size_t p_size) {
 
 
 	glBindBuffer(GL_ARRAY_BUFFER, resources.skeleton_transform_buffer);
 	glBindBuffer(GL_ARRAY_BUFFER, resources.skeleton_transform_buffer);

+ 1 - 6
drivers/gles2/rasterizer_storage_gles2.h

@@ -870,16 +870,12 @@ public:
 		Set<RasterizerScene::InstanceBase *> instances;
 		Set<RasterizerScene::InstanceBase *> instances;
 
 
 		Transform2D base_transform_2d;
 		Transform2D base_transform_2d;
-		Transform world_transform;
-		Transform world_transform_inverse;
-		bool use_world_transform;
 
 
 		Skeleton() :
 		Skeleton() :
 				use_2d(false),
 				use_2d(false),
 				size(0),
 				size(0),
 				tex_id(0),
 				tex_id(0),
-				update_list(this),
-				use_world_transform(false) {
+				update_list(this) {
 		}
 		}
 	};
 	};
 
 
@@ -897,7 +893,6 @@ public:
 	virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform);
 	virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform);
 	virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const;
 	virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const;
 	virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform);
 	virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform);
-	virtual void skeleton_set_world_transform(RID p_skeleton, bool p_enable, const Transform &p_world_transform);
 
 
 	void _update_skeleton_transform_buffer(const PoolVector<float> &p_data, size_t p_size);
 	void _update_skeleton_transform_buffer(const PoolVector<float> &p_data, size_t p_size);
 
 

+ 3 - 9
drivers/gles2/shaders/scene.glsl

@@ -61,9 +61,6 @@ uniform ivec2 skeleton_texture_size;
 
 
 #endif
 #endif
 
 
-uniform highp mat4 skeleton_transform;
-uniform highp mat4 skeleton_transform_inverse;
-uniform bool skeleton_in_world_coords;
 
 
 #endif
 #endif
 
 
@@ -410,12 +407,9 @@ void main() {
 
 
 #endif
 #endif
 
 
-	if (skeleton_in_world_coords) {
-		bone_transform = skeleton_transform * (bone_transform * skeleton_transform_inverse);
-		world_matrix = bone_transform * world_matrix;
-	} else {
-		world_matrix = world_matrix * bone_transform;
-	}
+
+	world_matrix = world_matrix * bone_transform;
+
 
 
 #endif
 #endif
 
 

+ 0 - 5
drivers/gles3/rasterizer_scene_gles3.cpp

@@ -2260,11 +2260,6 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
 
 
 		_set_cull(e->sort_key & RenderList::SORT_KEY_MIRROR_FLAG, e->sort_key & RenderList::SORT_KEY_CULL_DISABLED_FLAG, p_reverse_cull);
 		_set_cull(e->sort_key & RenderList::SORT_KEY_MIRROR_FLAG, e->sort_key & RenderList::SORT_KEY_CULL_DISABLED_FLAG, p_reverse_cull);
 
 
-		if (skeleton) {
-			state.scene_shader.set_uniform(SceneShaderGLES3::SKELETON_TRANSFORM, skeleton->world_transform);
-			state.scene_shader.set_uniform(SceneShaderGLES3::SKELETON_IN_WORLD_COORDS, skeleton->use_world_transform);
-		}
-
 		state.scene_shader.set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, e->instance->transform);
 		state.scene_shader.set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, e->instance->transform);
 
 
 		_render_geometry(e);
 		_render_geometry(e);

+ 0 - 14
drivers/gles3/rasterizer_storage_gles3.cpp

@@ -5162,20 +5162,6 @@ void RasterizerStorageGLES3::skeleton_set_base_transform_2d(RID p_skeleton, cons
 	skeleton->base_transform_2d = p_base_transform;
 	skeleton->base_transform_2d = p_base_transform;
 }
 }
 
 
-void RasterizerStorageGLES3::skeleton_set_world_transform(RID p_skeleton, bool p_enable, const Transform &p_world_transform) {
-
-	Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
-
-	ERR_FAIL_COND(skeleton->use_2d);
-
-	skeleton->world_transform = p_world_transform;
-	skeleton->use_world_transform = p_enable;
-
-	if (!skeleton->update_list.in_list()) {
-		skeleton_update_list.add(&skeleton->update_list);
-	}
-}
-
 void RasterizerStorageGLES3::update_dirty_skeletons() {
 void RasterizerStorageGLES3::update_dirty_skeletons() {
 
 
 	glActiveTexture(GL_TEXTURE0);
 	glActiveTexture(GL_TEXTURE0);

+ 1 - 5
drivers/gles3/rasterizer_storage_gles3.h

@@ -892,15 +892,12 @@ public:
 		SelfList<Skeleton> update_list;
 		SelfList<Skeleton> update_list;
 		Set<RasterizerScene::InstanceBase *> instances; //instances using skeleton
 		Set<RasterizerScene::InstanceBase *> instances; //instances using skeleton
 		Transform2D base_transform_2d;
 		Transform2D base_transform_2d;
-		bool use_world_transform;
-		Transform world_transform;
 
 
 		Skeleton() :
 		Skeleton() :
 				use_2d(false),
 				use_2d(false),
 				size(0),
 				size(0),
 				texture(0),
 				texture(0),
-				update_list(this),
-				use_world_transform(false) {
+				update_list(this) {
 		}
 		}
 	};
 	};
 
 
@@ -918,7 +915,6 @@ public:
 	virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform);
 	virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform);
 	virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const;
 	virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const;
 	virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform);
 	virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform);
-	virtual void skeleton_set_world_transform(RID p_skeleton, bool p_enable, const Transform &p_world_transform);
 
 
 	/* Light API */
 	/* Light API */
 
 

+ 1 - 9
drivers/gles3/shaders/scene.glsl

@@ -302,8 +302,6 @@ out highp float dp_clip;
 
 
 #ifdef USE_SKELETON
 #ifdef USE_SKELETON
 uniform highp sampler2D skeleton_texture; // texunit:-1
 uniform highp sampler2D skeleton_texture; // texunit:-1
-uniform highp mat4 skeleton_transform;
-uniform bool skeleton_in_world_coords;
 #endif
 #endif
 
 
 out highp vec4 position_interp;
 out highp vec4 position_interp;
@@ -432,14 +430,8 @@ void main() {
 					 vec4(0.0, 0.0, 0.0, 1.0)) *
 					 vec4(0.0, 0.0, 0.0, 1.0)) *
 			 bone_weights.w;
 			 bone_weights.w;
 
 
-		if (skeleton_in_world_coords) {
-			highp mat4 bone_matrix = skeleton_transform * (transpose(m) * inverse(skeleton_transform));
-			world_matrix = bone_matrix * world_matrix;
-
-		} else {
+		world_matrix = world_matrix * transpose(m);
 
 
-			world_matrix = world_matrix * transpose(m);
-		}
 	}
 	}
 #endif
 #endif
 
 

+ 0 - 1
editor/import/editor_import_collada.cpp

@@ -176,7 +176,6 @@ Error ColladaImport::_create_scene_skeletons(Collada::Node *p_node) {
 
 
 		Skeleton *sk = memnew(Skeleton);
 		Skeleton *sk = memnew(Skeleton);
 		int bone = 0;
 		int bone = 0;
-		sk->set_use_bones_in_world_transform(true); // This improves compatibility in Collada
 		for (int i = 0; i < p_node->children.size(); i++) {
 		for (int i = 0; i < p_node->children.size(); i++) {
 
 
 			_populate_skeleton(sk, p_node->children[i], bone, -1);
 			_populate_skeleton(sk, p_node->children[i], bone, -1);

File diff suppressed because it is too large
+ 931 - 211
editor/import/editor_scene_importer_gltf.cpp


+ 143 - 72
editor/import/editor_scene_importer_gltf.h

@@ -36,11 +36,26 @@
 #include "scene/3d/spatial.h"
 #include "scene/3d/spatial.h"
 
 
 class AnimationPlayer;
 class AnimationPlayer;
+class BoneAttachment;
+class MeshInstance;
 
 
 class EditorSceneImporterGLTF : public EditorSceneImporter {
 class EditorSceneImporterGLTF : public EditorSceneImporter {
 
 
 	GDCLASS(EditorSceneImporterGLTF, EditorSceneImporter);
 	GDCLASS(EditorSceneImporterGLTF, EditorSceneImporter);
 
 
+	typedef int GLTFAccessorIndex;
+	typedef int GLTFAnimationIndex;
+	typedef int GLTFBufferIndex;
+	typedef int GLTFBufferViewIndex;
+	typedef int GLTFCameraIndex;
+	typedef int GLTFImageIndex;
+	typedef int GLTFMaterialIndex;
+	typedef int GLTFMeshIndex;
+	typedef int GLTFNodeIndex;
+	typedef int GLTFSkeletonIndex;
+	typedef int GLTFSkinIndex;
+	typedef int GLTFTextureIndex;
+
 	enum {
 	enum {
 		ARRAY_BUFFER = 34962,
 		ARRAY_BUFFER = 34962,
 		ELEMENT_ARRAY_BUFFER = 34963,
 		ELEMENT_ARRAY_BUFFER = 34963,
@@ -61,8 +76,8 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
 
 
 	};
 	};
 
 
-	String _get_component_type_name(uint32_t p_component);
-	int _get_component_type_size(int component_type);
+	String _get_component_type_name(const uint32_t p_component);
+	int _get_component_type_size(const int component_type);
 
 
 	enum GLTFType {
 	enum GLTFType {
 		TYPE_SCALAR,
 		TYPE_SCALAR,
@@ -74,60 +89,48 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
 		TYPE_MAT4,
 		TYPE_MAT4,
 	};
 	};
 
 
-	String _get_type_name(GLTFType p_component);
+	String _get_type_name(const GLTFType p_component);
 
 
 	struct GLTFNode {
 	struct GLTFNode {
+
 		//matrices need to be transformed to this
 		//matrices need to be transformed to this
-		int parent;
+		GLTFNodeIndex parent;
+		int height;
 
 
 		Transform xform;
 		Transform xform;
 		String name;
 		String name;
-		//Node *godot_node;
-		//int godot_bone_index;
-
-		int mesh;
-		int camera;
-		int skin;
-		//int skeleton_skin;
-		//int child_of_skeleton; // put as children of skeleton
-		//Vector<int> skeleton_children; //skeleton put as children of this
-
-		struct Joint {
-			int skin;
-			int bone;
-			int godot_bone_index;
-
-			Joint() {
-				skin = -1;
-				bone = -1;
-				godot_bone_index = -1;
-			}
-		};
 
 
-		Vector<Joint> joints;
+		GLTFMeshIndex mesh;
+		GLTFCameraIndex camera;
+		GLTFSkinIndex skin;
+
+		GLTFSkeletonIndex skeleton;
+		bool joint;
 
 
-		//keep them for animation
 		Vector3 translation;
 		Vector3 translation;
 		Quat rotation;
 		Quat rotation;
 		Vector3 scale;
 		Vector3 scale;
 
 
 		Vector<int> children;
 		Vector<int> children;
-		Vector<Node *> godot_nodes;
+
+		GLTFNodeIndex fake_joint_parent;
 
 
 		GLTFNode() :
 		GLTFNode() :
 				parent(-1),
 				parent(-1),
+				height(-1),
 				mesh(-1),
 				mesh(-1),
 				camera(-1),
 				camera(-1),
 				skin(-1),
 				skin(-1),
-				//skeleton_skin(-1),
-				//child_of_skeleton(-1),
-				scale(Vector3(1, 1, 1)) {
-		}
+				skeleton(-1),
+				joint(false),
+				translation(0, 0, 0),
+				scale(Vector3(1, 1, 1)),
+				fake_joint_parent(-1) {}
 	};
 	};
 
 
 	struct GLTFBufferView {
 	struct GLTFBufferView {
 
 
-		int buffer;
+		GLTFBufferIndex buffer;
 		int byte_offset;
 		int byte_offset;
 		int byte_length;
 		int byte_length;
 		int byte_stride;
 		int byte_stride;
@@ -135,7 +138,7 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
 		//matrices need to be transformed to this
 		//matrices need to be transformed to this
 
 
 		GLTFBufferView() :
 		GLTFBufferView() :
-				buffer(0),
+				buffer(-1),
 				byte_offset(0),
 				byte_offset(0),
 				byte_length(0),
 				byte_length(0),
 				byte_stride(0),
 				byte_stride(0),
@@ -145,7 +148,7 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
 
 
 	struct GLTFAccessor {
 	struct GLTFAccessor {
 
 
-		int buffer_view;
+		GLTFBufferViewIndex buffer_view;
 		int byte_offset;
 		int byte_offset;
 		int component_type;
 		int component_type;
 		bool normalized;
 		bool normalized;
@@ -160,8 +163,6 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
 		int sparse_values_buffer_view;
 		int sparse_values_buffer_view;
 		int sparse_values_byte_offset;
 		int sparse_values_byte_offset;
 
 
-		//matrices need to be transformed to this
-
 		GLTFAccessor() {
 		GLTFAccessor() {
 			buffer_view = 0;
 			buffer_view = 0;
 			byte_offset = 0;
 			byte_offset = 0;
@@ -176,27 +177,67 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
 		}
 		}
 	};
 	};
 	struct GLTFTexture {
 	struct GLTFTexture {
-		int src_image;
+		GLTFImageIndex src_image;
 	};
 	};
 
 
-	struct GLTFSkin {
+	struct GLTFSkeleton {
+		// The *synthesized* skeletons joints
+		Vector<GLTFNodeIndex> joints;
 
 
-		String name;
-		struct Bone {
-			Transform inverse_bind;
-			int node;
-		};
+		// The roots of the skeleton. If there are multiple, each root must have the same parent
+		// (ie roots are siblings)
+		Vector<GLTFNodeIndex> roots;
 
 
-		int skeleton;
-		Vector<Bone> bones;
+		// The created Skeleton for the scene
+		Skeleton *godot_skeleton;
 
 
-		//matrices need to be transformed to this
+		// Set of unique bone names for the skeleton
+		Set<String> unique_names;
 
 
-		GLTFSkin() {
-			skeleton = -1;
+		GLTFSkeleton() :
+				godot_skeleton(nullptr) {
 		}
 		}
 	};
 	};
 
 
+	struct GLTFSkin {
+		String name;
+
+		// The "skeleton" property defined in the gltf spec. -1 = Scene Root
+		GLTFNodeIndex skin_root;
+
+		Vector<GLTFNodeIndex> joints_original;
+		Vector<Transform> inverse_binds;
+
+		// Note: joints + non_joints should form a complete subtree, or subtrees with a common parent
+
+		// All nodes that are skins that are caught in-between the original joints
+		// (inclusive of joints_original)
+		Vector<GLTFNodeIndex> joints;
+
+		// All Nodes that are caught in-between skin joint nodes, and are not defined
+		// as joints by any skin
+		Vector<GLTFNodeIndex> non_joints;
+
+		// The roots of the skin. In the case of multiple roots, their parent *must*
+		// be the same (the roots must be siblings)
+		Vector<GLTFNodeIndex> roots;
+
+		// The GLTF Skeleton this Skin points to (after we determine skeletons)
+		GLTFSkeletonIndex skeleton;
+
+		// A mapping from the joint indices (in the order of joints_original) to the
+		// Godot Skeleton's bone_indices
+		Map<int, int> joint_i_to_bone_i;
+
+		// The Actual Skin that will be created as a mapping between the IBM's of this skin
+		// to the generated skeleton for the mesh instances.
+		Ref<Skin> godot_skin;
+
+		GLTFSkin() :
+				skin_root(-1),
+				skeleton(-1) {}
+	};
+
 	struct GLTFMesh {
 	struct GLTFMesh {
 		Ref<ArrayMesh> mesh;
 		Ref<ArrayMesh> mesh;
 		Vector<float> blend_weights;
 		Vector<float> blend_weights;
@@ -272,11 +313,10 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
 
 
 		Set<String> unique_names;
 		Set<String> unique_names;
 
 
+		Vector<GLTFSkeleton> skeletons;
 		Vector<GLTFAnimation> animations;
 		Vector<GLTFAnimation> animations;
 
 
-		Map<int, Vector<int> > skeleton_nodes;
-
-		//Map<int, Vector<int> > skin_users; //cache skin users
+		Map<GLTFNodeIndex, Node *> scene_nodes;
 
 
 		~GLTFState() {
 		~GLTFState() {
 			for (int i = 0; i < nodes.size(); i++) {
 			for (int i = 0; i < nodes.size(); i++) {
@@ -285,37 +325,38 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
 		}
 		}
 	};
 	};
 
 
+	String _sanitize_scene_name(const String &name);
 	String _gen_unique_name(GLTFState &state, const String &p_name);
 	String _gen_unique_name(GLTFState &state, const String &p_name);
 
 
-	Ref<Texture> _get_texture(GLTFState &state, int p_texture);
+	String _sanitize_bone_name(const String &name);
+	String _gen_unique_bone_name(GLTFState &state, const GLTFSkeletonIndex skel_i, const String &p_name);
+
+	Ref<Texture> _get_texture(GLTFState &state, const GLTFTextureIndex p_texture);
 
 
 	Error _parse_json(const String &p_path, GLTFState &state);
 	Error _parse_json(const String &p_path, GLTFState &state);
 	Error _parse_glb(const String &p_path, GLTFState &state);
 	Error _parse_glb(const String &p_path, GLTFState &state);
 
 
 	Error _parse_scenes(GLTFState &state);
 	Error _parse_scenes(GLTFState &state);
 	Error _parse_nodes(GLTFState &state);
 	Error _parse_nodes(GLTFState &state);
+
+	void _compute_node_heights(GLTFState &state);
+
 	Error _parse_buffers(GLTFState &state, const String &p_base_path);
 	Error _parse_buffers(GLTFState &state, const String &p_base_path);
 	Error _parse_buffer_views(GLTFState &state);
 	Error _parse_buffer_views(GLTFState &state);
 	GLTFType _get_type_from_str(const String &p_string);
 	GLTFType _get_type_from_str(const String &p_string);
 	Error _parse_accessors(GLTFState &state);
 	Error _parse_accessors(GLTFState &state);
-	Error _decode_buffer_view(GLTFState &state, int p_buffer_view, double *dst, int skip_every, int skip_bytes, int element_size, int count, GLTFType type, int component_count, int component_type, int component_size, bool normalized, int byte_offset, bool for_vertex);
-	Vector<double> _decode_accessor(GLTFState &state, int p_accessor, bool p_for_vertex);
-	PoolVector<float> _decode_accessor_as_floats(GLTFState &state, int p_accessor, bool p_for_vertex);
-	PoolVector<int> _decode_accessor_as_ints(GLTFState &state, int p_accessor, bool p_for_vertex);
-	PoolVector<Vector2> _decode_accessor_as_vec2(GLTFState &state, int p_accessor, bool p_for_vertex);
-	PoolVector<Vector3> _decode_accessor_as_vec3(GLTFState &state, int p_accessor, bool p_for_vertex);
-	PoolVector<Color> _decode_accessor_as_color(GLTFState &state, int p_accessor, bool p_for_vertex);
-	Vector<Quat> _decode_accessor_as_quat(GLTFState &state, int p_accessor, bool p_for_vertex);
-	Vector<Transform2D> _decode_accessor_as_xform2d(GLTFState &state, int p_accessor, bool p_for_vertex);
-	Vector<Basis> _decode_accessor_as_basis(GLTFState &state, int p_accessor, bool p_for_vertex);
-	Vector<Transform> _decode_accessor_as_xform(GLTFState &state, int p_accessor, bool p_for_vertex);
-
-	void _reparent_skeleton(GLTFState &state, int p_node, Vector<Skeleton *> &skeletons, Node *p_parent_node);
-	void _generate_bone(GLTFState &state, int p_node, Vector<Skeleton *> &skeletons, Node *p_parent_node);
-	void _generate_node(GLTFState &state, int p_node, Node *p_parent, Node *p_owner, Vector<Skeleton *> &skeletons);
-	void _import_animation(GLTFState &state, AnimationPlayer *ap, int index, int bake_fps, Vector<Skeleton *> skeletons);
-
-	Spatial *_generate_scene(GLTFState &state, int p_bake_fps);
+	Error _decode_buffer_view(GLTFState &state, double *dst, const GLTFBufferViewIndex p_buffer_view, const int skip_every, const int skip_bytes, const int element_size, const int count, const GLTFType type, const int component_count, const int component_type, const int component_size, const bool normalized, const int byte_offset, const bool for_vertex);
+
+	Vector<double> _decode_accessor(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex);
+	PoolVector<float> _decode_accessor_as_floats(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex);
+	PoolVector<int> _decode_accessor_as_ints(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex);
+	PoolVector<Vector2> _decode_accessor_as_vec2(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex);
+	PoolVector<Vector3> _decode_accessor_as_vec3(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex);
+	PoolVector<Color> _decode_accessor_as_color(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex);
+	Vector<Quat> _decode_accessor_as_quat(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex);
+	Vector<Transform2D> _decode_accessor_as_xform2d(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex);
+	Vector<Basis> _decode_accessor_as_basis(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex);
+	Vector<Transform> _decode_accessor_as_xform(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex);
 
 
 	Error _parse_meshes(GLTFState &state);
 	Error _parse_meshes(GLTFState &state);
 	Error _parse_images(GLTFState &state, const String &p_base_path);
 	Error _parse_images(GLTFState &state, const String &p_base_path);
@@ -323,16 +364,46 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
 
 
 	Error _parse_materials(GLTFState &state);
 	Error _parse_materials(GLTFState &state);
 
 
+	GLTFNodeIndex _find_highest_node(GLTFState &state, const Vector<GLTFNodeIndex> &subset);
+
+	bool _capture_nodes_in_skin(GLTFState &state, GLTFSkin &skin, const GLTFNodeIndex node_index);
+	void _capture_nodes_for_multirooted_skin(GLTFState &state, GLTFSkin &skin);
+	Error _expand_skin(GLTFState &state, GLTFSkin &skin);
+	Error _verify_skin(GLTFState &state, GLTFSkin &skin);
 	Error _parse_skins(GLTFState &state);
 	Error _parse_skins(GLTFState &state);
 
 
+	Error _determine_skeletons(GLTFState &state);
+	Error _reparent_non_joint_skeleton_subtrees(GLTFState &state, GLTFSkeleton &skeleton, const Vector<GLTFNodeIndex> &non_joints);
+	Error _reparent_to_fake_joint(GLTFState &state, GLTFSkeleton &skeleton, const GLTFNodeIndex node_index);
+	Error _determine_skeleton_roots(GLTFState &state, const GLTFSkeletonIndex skel_i);
+
+	Error _create_skeletons(GLTFState &state);
+	Error _map_skin_joints_indices_to_skeleton_bone_indices(GLTFState &state);
+
+	Error _create_skins(GLTFState &state);
+	bool _skins_are_same(const Ref<Skin> &skin_a, const Ref<Skin> &skin_b);
+	void _remove_duplicate_skins(GLTFState &state);
+
 	Error _parse_cameras(GLTFState &state);
 	Error _parse_cameras(GLTFState &state);
 
 
 	Error _parse_animations(GLTFState &state);
 	Error _parse_animations(GLTFState &state);
 
 
+	BoneAttachment *_generate_bone_attachment(GLTFState &state, Skeleton *skeleton, const GLTFNodeIndex node_index);
+	MeshInstance *_generate_mesh_instance(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index);
+	Camera *_generate_camera(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index);
+	Spatial *_generate_spatial(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index);
+
+	void _generate_scene_node(GLTFState &state, Node *scene_parent, Spatial *scene_root, const GLTFNodeIndex node_index);
+	Spatial *_generate_scene(GLTFState &state, const int p_bake_fps);
+
+	void _process_mesh_instances(GLTFState &state, Spatial *scene_root);
+
 	void _assign_scene_names(GLTFState &state);
 	void _assign_scene_names(GLTFState &state);
 
 
 	template <class T>
 	template <class T>
-	T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, GLTFAnimation::Interpolation p_interp);
+	T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp);
+
+	void _import_animation(GLTFState &state, AnimationPlayer *ap, const GLTFAnimationIndex index, const int bake_fps);
 
 
 public:
 public:
 	virtual uint32_t get_import_flags() const;
 	virtual uint32_t get_import_flags() const;

+ 2 - 1
editor/plugins/spatial_editor_plugin.h

@@ -58,6 +58,7 @@ public:
 		RID instance;
 		RID instance;
 		Ref<ArrayMesh> mesh;
 		Ref<ArrayMesh> mesh;
 		Ref<Material> material;
 		Ref<Material> material;
+		Ref<SkinReference> skin_reference;
 		RID skeleton;
 		RID skeleton;
 		bool billboard;
 		bool billboard;
 		bool unscaled;
 		bool unscaled;
@@ -101,7 +102,7 @@ protected:
 
 
 public:
 public:
 	void add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material, bool p_billboard = false);
 	void add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material, bool p_billboard = false);
-	void add_mesh(const Ref<ArrayMesh> &p_mesh, bool p_billboard = false, const RID &p_skeleton = RID(), const Ref<Material> &p_material = Ref<Material>());
+	void add_mesh(const Ref<ArrayMesh> &p_mesh, bool p_billboard = false, const Ref<SkinReference> &p_skin_reference = Ref<SkinReference>(), const Ref<Material> &p_material = Ref<Material>());
 	void add_collision_segments(const Vector<Vector3> &p_lines);
 	void add_collision_segments(const Vector<Vector3> &p_lines);
 	void add_collision_triangles(const Ref<TriangleMesh> &p_tmesh);
 	void add_collision_triangles(const Ref<TriangleMesh> &p_tmesh);
 	void add_unscaled_billboard(const Ref<Material> &p_material, float p_scale = 1);
 	void add_unscaled_billboard(const Ref<Material> &p_material, float p_scale = 1);

+ 7 - 7
editor/spatial_editor_gizmos.cpp

@@ -172,8 +172,8 @@ void EditorSpatialGizmo::Instance::create_instance(Spatial *p_base, bool p_hidde
 
 
 	instance = VS::get_singleton()->instance_create2(mesh->get_rid(), p_base->get_world()->get_scenario());
 	instance = VS::get_singleton()->instance_create2(mesh->get_rid(), p_base->get_world()->get_scenario());
 	VS::get_singleton()->instance_attach_object_instance_id(instance, p_base->get_instance_id());
 	VS::get_singleton()->instance_attach_object_instance_id(instance, p_base->get_instance_id());
-	if (skeleton.is_valid())
-		VS::get_singleton()->instance_attach_skeleton(instance, skeleton);
+	if (skin_reference.is_valid())
+		VS::get_singleton()->instance_attach_skeleton(instance, skin_reference->get_skeleton());
 	if (extra_margin)
 	if (extra_margin)
 		VS::get_singleton()->instance_set_extra_visibility_margin(instance, 1);
 		VS::get_singleton()->instance_set_extra_visibility_margin(instance, 1);
 	VS::get_singleton()->instance_geometry_set_cast_shadows_setting(instance, VS::SHADOW_CASTING_SETTING_OFF);
 	VS::get_singleton()->instance_geometry_set_cast_shadows_setting(instance, VS::SHADOW_CASTING_SETTING_OFF);
@@ -181,14 +181,14 @@ void EditorSpatialGizmo::Instance::create_instance(Spatial *p_base, bool p_hidde
 	VS::get_singleton()->instance_set_layer_mask(instance, layer); //gizmos are 26
 	VS::get_singleton()->instance_set_layer_mask(instance, layer); //gizmos are 26
 }
 }
 
 
-void EditorSpatialGizmo::add_mesh(const Ref<ArrayMesh> &p_mesh, bool p_billboard, const RID &p_skeleton, const Ref<Material> &p_material) {
+void EditorSpatialGizmo::add_mesh(const Ref<ArrayMesh> &p_mesh, bool p_billboard, const Ref<SkinReference> &p_skin_reference, const Ref<Material> &p_material) {
 
 
 	ERR_FAIL_COND(!spatial_node);
 	ERR_FAIL_COND(!spatial_node);
 	Instance ins;
 	Instance ins;
 
 
 	ins.billboard = p_billboard;
 	ins.billboard = p_billboard;
 	ins.mesh = p_mesh;
 	ins.mesh = p_mesh;
-	ins.skeleton = p_skeleton;
+	ins.skin_reference = p_skin_reference;
 	ins.material = p_material;
 	ins.material = p_material;
 	if (valid) {
 	if (valid) {
 		ins.create_instance(spatial_node, hidden);
 		ins.create_instance(spatial_node, hidden);
@@ -1802,7 +1802,7 @@ void SkeletonSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
 	}
 	}
 
 
 	Ref<ArrayMesh> m = surface_tool->commit();
 	Ref<ArrayMesh> m = surface_tool->commit();
-	p_gizmo->add_mesh(m, false, skel->get_skeleton());
+	p_gizmo->add_mesh(m, false, skel->register_skin(Ref<Skin>()));
 }
 }
 
 
 ////
 ////
@@ -3725,7 +3725,7 @@ void CollisionShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
 
 
 		Ref<ConcavePolygonShape> cs2 = s;
 		Ref<ConcavePolygonShape> cs2 = s;
 		Ref<ArrayMesh> mesh = cs2->get_debug_mesh();
 		Ref<ArrayMesh> mesh = cs2->get_debug_mesh();
-		p_gizmo->add_mesh(mesh, false, RID(), material);
+		p_gizmo->add_mesh(mesh, false, Ref<SkinReference>(), material);
 	}
 	}
 
 
 	if (Object::cast_to<RayShape>(*s)) {
 	if (Object::cast_to<RayShape>(*s)) {
@@ -3747,7 +3747,7 @@ void CollisionShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
 		Ref<HeightMapShape> hms = s;
 		Ref<HeightMapShape> hms = s;
 
 
 		Ref<ArrayMesh> mesh = hms->get_debug_mesh();
 		Ref<ArrayMesh> mesh = hms->get_debug_mesh();
-		p_gizmo->add_mesh(mesh, false, RID(), material);
+		p_gizmo->add_mesh(mesh, false, Ref<SkinReference>(), material);
 	}
 	}
 }
 }
 
 

+ 1 - 1
modules/csg/csg_gizmos.cpp

@@ -377,7 +377,7 @@ void CSGShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
 				break;
 				break;
 		}
 		}
 
 
-		p_gizmo->add_mesh(mesh, false, RID(), solid_material);
+		p_gizmo->add_mesh(mesh, false, Ref<SkinReference>(), solid_material);
 	}
 	}
 
 
 	if (Object::cast_to<CSGSphere>(cs)) {
 	if (Object::cast_to<CSGSphere>(cs)) {

+ 33 - 4
scene/3d/mesh_instance.cpp

@@ -149,12 +149,38 @@ Ref<Mesh> MeshInstance::get_mesh() const {
 
 
 void MeshInstance::_resolve_skeleton_path() {
 void MeshInstance::_resolve_skeleton_path() {
 
 
-	if (skeleton_path.is_empty())
+	Ref<SkinReference> new_skin_reference;
+
+	if (!skeleton_path.is_empty()) {
+		Skeleton *skeleton = Object::cast_to<Skeleton>(get_node(skeleton_path));
+		if (skeleton) {
+			new_skin_reference = skeleton->register_skin(skin);
+			if (skin.is_null()) {
+				//a skin was created for us
+				skin = new_skin_reference->get_skin();
+				_change_notify();
+			}
+		}
+	}
+
+	skin_ref = new_skin_reference;
+
+	if (skin_ref.is_valid()) {
+		VisualServer::get_singleton()->instance_attach_skeleton(get_instance(), skin_ref->get_skeleton());
+	} else {
+		VisualServer::get_singleton()->instance_attach_skeleton(get_instance(), RID());
+	}
+}
+
+void MeshInstance::set_skin(const Ref<Skin> &p_skin) {
+	skin = p_skin;
+	if (!is_inside_tree())
 		return;
 		return;
+	_resolve_skeleton_path();
+}
 
 
-	Skeleton *skeleton = Object::cast_to<Skeleton>(get_node(skeleton_path));
-	if (skeleton)
-		VisualServer::get_singleton()->instance_attach_skeleton(get_instance(), skeleton->get_skeleton());
+Ref<Skin> MeshInstance::get_skin() const {
+	return skin;
 }
 }
 
 
 void MeshInstance::set_skeleton_path(const NodePath &p_skeleton) {
 void MeshInstance::set_skeleton_path(const NodePath &p_skeleton) {
@@ -365,6 +391,8 @@ void MeshInstance::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_mesh"), &MeshInstance::get_mesh);
 	ClassDB::bind_method(D_METHOD("get_mesh"), &MeshInstance::get_mesh);
 	ClassDB::bind_method(D_METHOD("set_skeleton_path", "skeleton_path"), &MeshInstance::set_skeleton_path);
 	ClassDB::bind_method(D_METHOD("set_skeleton_path", "skeleton_path"), &MeshInstance::set_skeleton_path);
 	ClassDB::bind_method(D_METHOD("get_skeleton_path"), &MeshInstance::get_skeleton_path);
 	ClassDB::bind_method(D_METHOD("get_skeleton_path"), &MeshInstance::get_skeleton_path);
+	ClassDB::bind_method(D_METHOD("set_skin", "skin"), &MeshInstance::set_skin);
+	ClassDB::bind_method(D_METHOD("get_skin"), &MeshInstance::get_skin);
 
 
 	ClassDB::bind_method(D_METHOD("get_surface_material_count"), &MeshInstance::get_surface_material_count);
 	ClassDB::bind_method(D_METHOD("get_surface_material_count"), &MeshInstance::get_surface_material_count);
 	ClassDB::bind_method(D_METHOD("set_surface_material", "surface", "material"), &MeshInstance::set_surface_material);
 	ClassDB::bind_method(D_METHOD("set_surface_material", "surface", "material"), &MeshInstance::set_surface_material);
@@ -380,6 +408,7 @@ void MeshInstance::_bind_methods() {
 	ClassDB::set_method_flags("MeshInstance", "create_debug_tangents", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
 	ClassDB::set_method_flags("MeshInstance", "create_debug_tangents", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
 
 
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
+	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "skin", PROPERTY_HINT_RESOURCE_TYPE, "Skin"), "set_skin", "get_skin");
 	ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton"), "set_skeleton_path", "get_skeleton_path");
 	ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton"), "set_skeleton_path", "get_skeleton_path");
 }
 }
 
 

+ 7 - 0
scene/3d/mesh_instance.h

@@ -31,8 +31,10 @@
 #ifndef MESH_INSTANCE_H
 #ifndef MESH_INSTANCE_H
 #define MESH_INSTANCE_H
 #define MESH_INSTANCE_H
 
 
+#include "scene/3d/skeleton.h"
 #include "scene/3d/visual_instance.h"
 #include "scene/3d/visual_instance.h"
 #include "scene/resources/mesh.h"
 #include "scene/resources/mesh.h"
+#include "scene/resources/skin.h"
 
 
 class MeshInstance : public GeometryInstance {
 class MeshInstance : public GeometryInstance {
 
 
@@ -40,6 +42,8 @@ class MeshInstance : public GeometryInstance {
 
 
 protected:
 protected:
 	Ref<Mesh> mesh;
 	Ref<Mesh> mesh;
+	Ref<Skin> skin;
+	Ref<SkinReference> skin_ref;
 	NodePath skeleton_path;
 	NodePath skeleton_path;
 
 
 	struct BlendShapeTrack {
 	struct BlendShapeTrack {
@@ -70,6 +74,9 @@ public:
 	void set_mesh(const Ref<Mesh> &p_mesh);
 	void set_mesh(const Ref<Mesh> &p_mesh);
 	Ref<Mesh> get_mesh() const;
 	Ref<Mesh> get_mesh() const;
 
 
+	void set_skin(const Ref<Skin> &p_skin);
+	Ref<Skin> get_skin() const;
+
 	void set_skeleton_path(const NodePath &p_skeleton);
 	void set_skeleton_path(const NodePath &p_skeleton);
 	NodePath get_skeleton_path();
 	NodePath get_skeleton_path();
 
 

+ 3 - 4
scene/3d/physics_body.cpp

@@ -2182,7 +2182,7 @@ void PhysicalBone::_notification(int p_what) {
 
 
 void PhysicalBone::_direct_state_changed(Object *p_state) {
 void PhysicalBone::_direct_state_changed(Object *p_state) {
 
 
-	if (!simulate_physics) {
+	if (!simulate_physics || !_internal_simulate_physics) {
 		return;
 		return;
 	}
 	}
 
 
@@ -2205,7 +2205,7 @@ void PhysicalBone::_direct_state_changed(Object *p_state) {
 	// Update skeleton
 	// Update skeleton
 	if (parent_skeleton) {
 	if (parent_skeleton) {
 		if (-1 != bone_id) {
 		if (-1 != bone_id) {
-			parent_skeleton->set_bone_global_pose(bone_id, parent_skeleton->get_global_transform().affine_inverse() * (global_transform * body_offset_inverse));
+			parent_skeleton->set_bone_global_pose_override(bone_id, parent_skeleton->get_global_transform().affine_inverse() * (global_transform * body_offset_inverse), 1.0, true);
 		}
 		}
 	}
 	}
 }
 }
@@ -2716,7 +2716,6 @@ void PhysicalBone::_start_physics_simulation() {
 	PhysicsServer::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer());
 	PhysicsServer::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer());
 	PhysicsServer::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask());
 	PhysicsServer::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask());
 	PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed");
 	PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed");
-	parent_skeleton->set_bone_ignore_animation(bone_id, true);
 	_internal_simulate_physics = true;
 	_internal_simulate_physics = true;
 }
 }
 
 
@@ -2728,6 +2727,6 @@ void PhysicalBone::_stop_physics_simulation() {
 	PhysicsServer::get_singleton()->body_set_collision_layer(get_rid(), 0);
 	PhysicsServer::get_singleton()->body_set_collision_layer(get_rid(), 0);
 	PhysicsServer::get_singleton()->body_set_collision_mask(get_rid(), 0);
 	PhysicsServer::get_singleton()->body_set_collision_mask(get_rid(), 0);
 	PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(), NULL, "");
 	PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(), NULL, "");
-	parent_skeleton->set_bone_ignore_animation(bone_id, false);
+	parent_skeleton->set_bone_global_pose_override(bone_id, Transform(), 0.0, false);
 	_internal_simulate_physics = false;
 	_internal_simulate_physics = false;
 }
 }

+ 155 - 148
scene/3d/skeleton.cpp

@@ -36,6 +36,34 @@
 #include "scene/3d/physics_body.h"
 #include "scene/3d/physics_body.h"
 #include "scene/resources/surface_tool.h"
 #include "scene/resources/surface_tool.h"
 
 
+void SkinReference::_skin_changed() {
+	if (skeleton_node) {
+		skeleton_node->_make_dirty();
+	}
+}
+
+void SkinReference::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("_skin_changed"), &SkinReference::_skin_changed);
+	ClassDB::bind_method(D_METHOD("get_skeleton"), &SkinReference::get_skeleton);
+	ClassDB::bind_method(D_METHOD("get_skin"), &SkinReference::get_skin);
+}
+
+RID SkinReference::get_skeleton() const {
+	return skeleton;
+}
+
+Ref<Skin> SkinReference::get_skin() const {
+	return skin;
+}
+
+SkinReference::~SkinReference() {
+	if (skeleton_node) {
+		skeleton_node->skin_bindings.erase(this);
+	}
+
+	VS::get_singleton()->free(skeleton);
+}
+
 bool Skeleton::_set(const StringName &p_path, const Variant &p_value) {
 bool Skeleton::_set(const StringName &p_path, const Variant &p_value) {
 
 
 	String path = p_path;
 	String path = p_path;
@@ -196,110 +224,77 @@ void Skeleton::_notification(int p_what) {
 
 
 	switch (p_what) {
 	switch (p_what) {
 
 
-		case NOTIFICATION_ENTER_WORLD: {
-
-			VS::get_singleton()->skeleton_set_world_transform(skeleton, use_bones_in_world_transform, get_global_transform());
-
-		} break;
-		case NOTIFICATION_EXIT_WORLD: {
-
-		} break;
-		case NOTIFICATION_TRANSFORM_CHANGED: {
-
-			VS::get_singleton()->skeleton_set_world_transform(skeleton, use_bones_in_world_transform, get_global_transform());
-		} break;
 		case NOTIFICATION_UPDATE_SKELETON: {
 		case NOTIFICATION_UPDATE_SKELETON: {
 
 
 			VisualServer *vs = VisualServer::get_singleton();
 			VisualServer *vs = VisualServer::get_singleton();
 			Bone *bonesptr = bones.ptrw();
 			Bone *bonesptr = bones.ptrw();
 			int len = bones.size();
 			int len = bones.size();
 
 
-			vs->skeleton_allocate(skeleton, len); // if same size, nothing really happens
-
 			_update_process_order();
 			_update_process_order();
 
 
 			const int *order = process_order.ptr();
 			const int *order = process_order.ptr();
 
 
-			// pose changed, rebuild cache of inverses
-			if (rest_global_inverse_dirty) {
-
-				// calculate global rests and invert them
-				for (int i = 0; i < len; i++) {
-					Bone &b = bonesptr[order[i]];
-					if (b.parent >= 0)
-						b.rest_global_inverse = bonesptr[b.parent].rest_global_inverse * b.rest;
-					else
-						b.rest_global_inverse = b.rest;
-				}
-				for (int i = 0; i < len; i++) {
-					Bone &b = bonesptr[order[i]];
-					b.rest_global_inverse.affine_invert();
-				}
-
-				rest_global_inverse_dirty = false;
-			}
-
 			for (int i = 0; i < len; i++) {
 			for (int i = 0; i < len; i++) {
 
 
 				Bone &b = bonesptr[order[i]];
 				Bone &b = bonesptr[order[i]];
 
 
-				if (b.disable_rest) {
-					if (b.enabled) {
+				if (b.global_pose_override_amount >= 0.999) {
+					b.pose_global = b.global_pose_override;
+				} else {
+					if (b.disable_rest) {
+						if (b.enabled) {
 
 
-						Transform pose = b.pose;
-						if (b.custom_pose_enable) {
+							Transform pose = b.pose;
+							if (b.parent >= 0) {
 
 
-							pose = b.custom_pose * pose;
-						}
+								b.pose_global = bonesptr[b.parent].pose_global * pose;
+							} else {
 
 
-						if (b.parent >= 0) {
-
-							b.pose_global = bonesptr[b.parent].pose_global * pose;
+								b.pose_global = pose;
+							}
 						} else {
 						} else {
 
 
-							b.pose_global = pose;
-						}
-					} else {
-
-						if (b.parent >= 0) {
+							if (b.parent >= 0) {
 
 
-							b.pose_global = bonesptr[b.parent].pose_global;
-						} else {
+								b.pose_global = bonesptr[b.parent].pose_global;
+							} else {
 
 
-							b.pose_global = Transform();
+								b.pose_global = Transform();
+							}
 						}
 						}
-					}
 
 
-				} else {
-					if (b.enabled) {
+					} else {
+						if (b.enabled) {
 
 
-						Transform pose = b.pose;
-						if (b.custom_pose_enable) {
+							Transform pose = b.pose;
 
 
-							pose = b.custom_pose * pose;
-						}
+							if (b.parent >= 0) {
 
 
-						if (b.parent >= 0) {
+								b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose);
+							} else {
 
 
-							b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose);
+								b.pose_global = b.rest * pose;
+							}
 						} else {
 						} else {
 
 
-							b.pose_global = b.rest * pose;
-						}
-					} else {
+							if (b.parent >= 0) {
 
 
-						if (b.parent >= 0) {
+								b.pose_global = bonesptr[b.parent].pose_global * b.rest;
+							} else {
 
 
-							b.pose_global = bonesptr[b.parent].pose_global * b.rest;
-						} else {
-
-							b.pose_global = b.rest;
+								b.pose_global = b.rest;
+							}
 						}
 						}
 					}
 					}
+
+					if (b.global_pose_override_amount >= CMP_EPSILON) {
+						b.pose_global = b.pose_global.interpolate_with(b.global_pose_override, b.global_pose_override_amount);
+					}
 				}
 				}
 
 
-				b.transform_final = b.pose_global * b.rest_global_inverse;
-				vs->skeleton_bone_set_transform(skeleton, order[i], b.transform_final);
+				if (b.global_pose_override_reset) {
+					b.global_pose_override_amount = 0.0;
+				}
 
 
 				for (List<uint32_t>::Element *E = b.nodes_bound.front(); E; E = E->next()) {
 				for (List<uint32_t>::Element *E = b.nodes_bound.front(); E; E = E->next()) {
 
 
@@ -311,28 +306,37 @@ void Skeleton::_notification(int p_what) {
 				}
 				}
 			}
 			}
 
 
+			//update skins
+			for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) {
+
+				const Skin *skin = E->get()->skin.operator->();
+				RID skeleton = E->get()->skeleton;
+				uint32_t bind_count = skin->get_bind_count();
+
+				if (E->get()->bind_count != bind_count) {
+					VS::get_singleton()->skeleton_allocate(skeleton, bind_count);
+					E->get()->bind_count = bind_count;
+				}
+
+				for (uint32_t i = 0; i < bind_count; i++) {
+					uint32_t bone_index = skin->get_bind_bone(i);
+					ERR_CONTINUE(bone_index >= (uint32_t)len);
+					vs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].pose_global * skin->get_bind_pose(i));
+				}
+			}
+
 			dirty = false;
 			dirty = false;
 		} break;
 		} break;
 	}
 	}
 }
 }
 
 
-Transform Skeleton::get_bone_transform(int p_bone) const {
-	ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform());
-	if (dirty)
-		const_cast<Skeleton *>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
-	return bones[p_bone].pose_global * bones[p_bone].rest_global_inverse;
-}
-
-void Skeleton::set_bone_global_pose(int p_bone, const Transform &p_pose) {
+void Skeleton::set_bone_global_pose_override(int p_bone, const Transform &p_pose, float p_amount, bool p_persistent) {
 
 
 	ERR_FAIL_INDEX(p_bone, bones.size());
 	ERR_FAIL_INDEX(p_bone, bones.size());
-	if (bones[p_bone].parent == -1) {
-
-		set_bone_pose(p_bone, bones[p_bone].rest_global_inverse * p_pose); //fast
-	} else {
-
-		set_bone_pose(p_bone, bones[p_bone].rest.affine_inverse() * (get_bone_global_pose(bones[p_bone].parent).affine_inverse() * p_pose)); //slow
-	}
+	bones.write[p_bone].global_pose_override_amount = p_amount;
+	bones.write[p_bone].global_pose_override = p_pose;
+	bones.write[p_bone].global_pose_override_reset = !p_persistent;
+	_make_dirty();
 }
 }
 
 
 Transform Skeleton::get_bone_global_pose(int p_bone) const {
 Transform Skeleton::get_bone_global_pose(int p_bone) const {
@@ -343,11 +347,6 @@ Transform Skeleton::get_bone_global_pose(int p_bone) const {
 	return bones[p_bone].pose_global;
 	return bones[p_bone].pose_global;
 }
 }
 
 
-RID Skeleton::get_skeleton() const {
-
-	return skeleton;
-}
-
 // skeleton creation api
 // skeleton creation api
 void Skeleton::add_bone(const String &p_name) {
 void Skeleton::add_bone(const String &p_name) {
 
 
@@ -362,8 +361,6 @@ void Skeleton::add_bone(const String &p_name) {
 	b.name = p_name;
 	b.name = p_name;
 	bones.push_back(b);
 	bones.push_back(b);
 	process_order_dirty = true;
 	process_order_dirty = true;
-
-	rest_global_inverse_dirty = true;
 	_make_dirty();
 	_make_dirty();
 	update_gizmo();
 	update_gizmo();
 }
 }
@@ -408,7 +405,6 @@ void Skeleton::set_bone_parent(int p_bone, int p_parent) {
 	ERR_FAIL_COND(p_parent != -1 && (p_parent < 0));
 	ERR_FAIL_COND(p_parent != -1 && (p_parent < 0));
 
 
 	bones.write[p_bone].parent = p_parent;
 	bones.write[p_bone].parent = p_parent;
-	rest_global_inverse_dirty = true;
 	process_order_dirty = true;
 	process_order_dirty = true;
 	_make_dirty();
 	_make_dirty();
 }
 }
@@ -426,23 +422,11 @@ void Skeleton::unparent_bone_and_rest(int p_bone) {
 	}
 	}
 
 
 	bones.write[p_bone].parent = -1;
 	bones.write[p_bone].parent = -1;
-	bones.write[p_bone].rest_global_inverse = bones[p_bone].rest.affine_inverse(); //same thing
 	process_order_dirty = true;
 	process_order_dirty = true;
 
 
 	_make_dirty();
 	_make_dirty();
 }
 }
 
 
-void Skeleton::set_bone_ignore_animation(int p_bone, bool p_ignore) {
-	ERR_FAIL_INDEX(p_bone, bones.size());
-	bones.write[p_bone].ignore_animation = p_ignore;
-}
-
-bool Skeleton::is_bone_ignore_animation(int p_bone) const {
-
-	ERR_FAIL_INDEX_V(p_bone, bones.size(), false);
-	return bones[p_bone].ignore_animation;
-}
-
 void Skeleton::set_bone_disable_rest(int p_bone, bool p_disable) {
 void Skeleton::set_bone_disable_rest(int p_bone, bool p_disable) {
 
 
 	ERR_FAIL_INDEX(p_bone, bones.size());
 	ERR_FAIL_INDEX(p_bone, bones.size());
@@ -467,7 +451,6 @@ void Skeleton::set_bone_rest(int p_bone, const Transform &p_rest) {
 	ERR_FAIL_INDEX(p_bone, bones.size());
 	ERR_FAIL_INDEX(p_bone, bones.size());
 
 
 	bones.write[p_bone].rest = p_rest;
 	bones.write[p_bone].rest = p_rest;
-	rest_global_inverse_dirty = true;
 	_make_dirty();
 	_make_dirty();
 }
 }
 Transform Skeleton::get_bone_rest(int p_bone) const {
 Transform Skeleton::get_bone_rest(int p_bone) const {
@@ -482,7 +465,6 @@ void Skeleton::set_bone_enabled(int p_bone, bool p_enabled) {
 	ERR_FAIL_INDEX(p_bone, bones.size());
 	ERR_FAIL_INDEX(p_bone, bones.size());
 
 
 	bones.write[p_bone].enabled = p_enabled;
 	bones.write[p_bone].enabled = p_enabled;
-	rest_global_inverse_dirty = true;
 	_make_dirty();
 	_make_dirty();
 }
 }
 bool Skeleton::is_bone_enabled(int p_bone) const {
 bool Skeleton::is_bone_enabled(int p_bone) const {
@@ -529,7 +511,6 @@ void Skeleton::get_bound_child_nodes_to_bone(int p_bone, List<Node *> *p_bound)
 void Skeleton::clear_bones() {
 void Skeleton::clear_bones() {
 
 
 	bones.clear();
 	bones.clear();
-	rest_global_inverse_dirty = true;
 	process_order_dirty = true;
 	process_order_dirty = true;
 
 
 	_make_dirty();
 	_make_dirty();
@@ -552,23 +533,6 @@ Transform Skeleton::get_bone_pose(int p_bone) const {
 	return bones[p_bone].pose;
 	return bones[p_bone].pose;
 }
 }
 
 
-void Skeleton::set_bone_custom_pose(int p_bone, const Transform &p_custom_pose) {
-
-	ERR_FAIL_INDEX(p_bone, bones.size());
-	//ERR_FAIL_COND( !is_inside_scene() );
-
-	bones.write[p_bone].custom_pose_enable = (p_custom_pose != Transform());
-	bones.write[p_bone].custom_pose = p_custom_pose;
-
-	_make_dirty();
-}
-
-Transform Skeleton::get_bone_custom_pose(int p_bone) const {
-
-	ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform());
-	return bones[p_bone].custom_pose;
-}
-
 void Skeleton::_make_dirty() {
 void Skeleton::_make_dirty() {
 
 
 	if (dirty)
 	if (dirty)
@@ -747,14 +711,66 @@ void Skeleton::physical_bones_remove_collision_exception(RID p_exception) {
 
 
 #endif // _3D_DISABLED
 #endif // _3D_DISABLED
 
 
-void Skeleton::set_use_bones_in_world_transform(bool p_enable) {
-	use_bones_in_world_transform = p_enable;
-	if (is_inside_tree()) {
-		VS::get_singleton()->skeleton_set_world_transform(skeleton, use_bones_in_world_transform, get_global_transform());
-	}
+void Skeleton::_skin_changed() {
+	_make_dirty();
 }
 }
-bool Skeleton::is_using_bones_in_world_transform() const {
-	return use_bones_in_world_transform;
+
+Ref<SkinReference> Skeleton::register_skin(const Ref<Skin> &p_skin) {
+
+	for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) {
+		if (E->get()->skin == p_skin) {
+			return Ref<SkinReference>(E->get());
+		}
+	}
+
+	Ref<Skin> skin = p_skin;
+
+	if (skin.is_null()) {
+		//need to create one from existing code, this is for compatibility only
+		//when skeletons did not support skins. It is also used by gizmo
+		//to display the skeleton.
+
+		skin.instance();
+		skin->set_bind_count(bones.size());
+		_update_process_order(); //just in case
+
+		// pose changed, rebuild cache of inverses
+		const Bone *bonesptr = bones.ptr();
+		int len = bones.size();
+		const int *order = process_order.ptr();
+
+		// calculate global rests and invert them
+		for (int i = 0; i < len; i++) {
+			const Bone &b = bonesptr[order[i]];
+			if (b.parent >= 0) {
+				skin->set_bind_pose(order[i], skin->get_bind_pose(b.parent) * b.rest);
+			} else {
+				skin->set_bind_pose(order[i], b.rest);
+			}
+		}
+
+		for (int i = 0; i < len; i++) {
+			//the inverse is what is actually required
+			skin->set_bind_bone(i, i);
+			skin->set_bind_pose(i, skin->get_bind_pose(i).affine_inverse());
+		}
+	}
+
+	ERR_FAIL_COND_V(skin.is_null(), Ref<SkinReference>());
+
+	Ref<SkinReference> skin_ref;
+	skin_ref.instance();
+
+	skin_ref->skeleton_node = this;
+	skin_ref->bind_count = 0;
+	skin_ref->skeleton = VisualServer::get_singleton()->skeleton_create();
+	skin_ref->skeleton_node = this;
+	skin_ref->skin = skin;
+
+	skin_bindings.insert(skin_ref.operator->());
+
+	skin->connect("changed", skin_ref.operator->(), "_skin_changed");
+	return skin_ref;
 }
 }
 
 
 void Skeleton::_bind_methods() {
 void Skeleton::_bind_methods() {
@@ -773,6 +789,8 @@ void Skeleton::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_bone_rest", "bone_idx"), &Skeleton::get_bone_rest);
 	ClassDB::bind_method(D_METHOD("get_bone_rest", "bone_idx"), &Skeleton::get_bone_rest);
 	ClassDB::bind_method(D_METHOD("set_bone_rest", "bone_idx", "rest"), &Skeleton::set_bone_rest);
 	ClassDB::bind_method(D_METHOD("set_bone_rest", "bone_idx", "rest"), &Skeleton::set_bone_rest);
 
 
+	ClassDB::bind_method(D_METHOD("register_skin", "skin"), &Skeleton::register_skin);
+
 	ClassDB::bind_method(D_METHOD("localize_rests"), &Skeleton::localize_rests);
 	ClassDB::bind_method(D_METHOD("localize_rests"), &Skeleton::localize_rests);
 
 
 	ClassDB::bind_method(D_METHOD("set_bone_disable_rest", "bone_idx", "disable"), &Skeleton::set_bone_disable_rest);
 	ClassDB::bind_method(D_METHOD("set_bone_disable_rest", "bone_idx", "disable"), &Skeleton::set_bone_disable_rest);
@@ -787,17 +805,9 @@ void Skeleton::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_bone_pose", "bone_idx"), &Skeleton::get_bone_pose);
 	ClassDB::bind_method(D_METHOD("get_bone_pose", "bone_idx"), &Skeleton::get_bone_pose);
 	ClassDB::bind_method(D_METHOD("set_bone_pose", "bone_idx", "pose"), &Skeleton::set_bone_pose);
 	ClassDB::bind_method(D_METHOD("set_bone_pose", "bone_idx", "pose"), &Skeleton::set_bone_pose);
 
 
-	ClassDB::bind_method(D_METHOD("set_bone_global_pose", "bone_idx", "pose"), &Skeleton::set_bone_global_pose);
+	ClassDB::bind_method(D_METHOD("set_bone_global_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton::set_bone_global_pose_override, DEFVAL(false));
 	ClassDB::bind_method(D_METHOD("get_bone_global_pose", "bone_idx"), &Skeleton::get_bone_global_pose);
 	ClassDB::bind_method(D_METHOD("get_bone_global_pose", "bone_idx"), &Skeleton::get_bone_global_pose);
 
 
-	ClassDB::bind_method(D_METHOD("get_bone_custom_pose", "bone_idx"), &Skeleton::get_bone_custom_pose);
-	ClassDB::bind_method(D_METHOD("set_bone_custom_pose", "bone_idx", "custom_pose"), &Skeleton::set_bone_custom_pose);
-
-	ClassDB::bind_method(D_METHOD("get_bone_transform", "bone_idx"), &Skeleton::get_bone_transform);
-
-	ClassDB::bind_method(D_METHOD("set_use_bones_in_world_transform", "enable"), &Skeleton::set_use_bones_in_world_transform);
-	ClassDB::bind_method(D_METHOD("is_using_bones_in_world_transform"), &Skeleton::is_using_bones_in_world_transform);
-
 #ifndef _3D_DISABLED
 #ifndef _3D_DISABLED
 
 
 	ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation"), &Skeleton::physical_bones_stop_simulation);
 	ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation"), &Skeleton::physical_bones_stop_simulation);
@@ -807,22 +817,19 @@ void Skeleton::_bind_methods() {
 
 
 #endif // _3D_DISABLED
 #endif // _3D_DISABLED
 
 
-	ClassDB::bind_method(D_METHOD("set_bone_ignore_animation", "bone", "ignore"), &Skeleton::set_bone_ignore_animation);
-
-	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bones_in_world_transform"), "set_use_bones_in_world_transform", "is_using_bones_in_world_transform");
 	BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON);
 	BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON);
 }
 }
 
 
 Skeleton::Skeleton() {
 Skeleton::Skeleton() {
 
 
-	rest_global_inverse_dirty = true;
 	dirty = false;
 	dirty = false;
 	process_order_dirty = true;
 	process_order_dirty = true;
-	skeleton = VisualServer::get_singleton()->skeleton_create();
-	set_notify_transform(true);
-	use_bones_in_world_transform = false;
 }
 }
 
 
 Skeleton::~Skeleton() {
 Skeleton::~Skeleton() {
-	VisualServer::get_singleton()->free(skeleton);
+
+	//some skins may remain bound
+	for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) {
+		E->get()->skeleton_node = nullptr;
+	}
 }
 }

+ 36 - 26
scene/3d/skeleton.h

@@ -33,6 +33,7 @@
 
 
 #include "core/rid.h"
 #include "core/rid.h"
 #include "scene/3d/spatial.h"
 #include "scene/3d/spatial.h"
+#include "scene/resources/skin.h"
 
 
 #ifndef _3D_DISABLED
 #ifndef _3D_DISABLED
 typedef int BoneId;
 typedef int BoneId;
@@ -40,10 +41,38 @@ typedef int BoneId;
 class PhysicalBone;
 class PhysicalBone;
 #endif // _3D_DISABLED
 #endif // _3D_DISABLED
 
 
+class Skeleton;
+
+class SkinReference : public Reference {
+	GDCLASS(SkinReference, Reference)
+	friend class Skeleton;
+
+	Skeleton *skeleton_node;
+	RID skeleton;
+	Ref<Skin> skin;
+	uint32_t bind_count = 0;
+	void _skin_changed();
+
+protected:
+	static void _bind_methods();
+
+public:
+	RID get_skeleton() const;
+	Ref<Skin> get_skin() const;
+	~SkinReference();
+};
+
 class Skeleton : public Spatial {
 class Skeleton : public Spatial {
 
 
 	GDCLASS(Skeleton, Spatial);
 	GDCLASS(Skeleton, Spatial);
 
 
+private:
+	friend class SkinReference;
+
+	Set<SkinReference *> skin_bindings;
+
+	void _skin_changed();
+
 	struct Bone {
 	struct Bone {
 
 
 		String name;
 		String name;
@@ -52,19 +81,15 @@ class Skeleton : public Spatial {
 		int parent;
 		int parent;
 		int sort_index; //used for re-sorting process order
 		int sort_index; //used for re-sorting process order
 
 
-		bool ignore_animation;
-
 		bool disable_rest;
 		bool disable_rest;
 		Transform rest;
 		Transform rest;
-		Transform rest_global_inverse;
 
 
 		Transform pose;
 		Transform pose;
 		Transform pose_global;
 		Transform pose_global;
 
 
-		bool custom_pose_enable;
-		Transform custom_pose;
-
-		Transform transform_final;
+		float global_pose_override_amount;
+		bool global_pose_override_reset;
+		Transform global_pose_override;
 
 
 #ifndef _3D_DISABLED
 #ifndef _3D_DISABLED
 		PhysicalBone *physical_bone;
 		PhysicalBone *physical_bone;
@@ -76,9 +101,9 @@ class Skeleton : public Spatial {
 		Bone() {
 		Bone() {
 			parent = -1;
 			parent = -1;
 			enabled = true;
 			enabled = true;
-			ignore_animation = false;
-			custom_pose_enable = false;
 			disable_rest = false;
 			disable_rest = false;
+			global_pose_override_amount = 0;
+			global_pose_override_reset = false;
 #ifndef _3D_DISABLED
 #ifndef _3D_DISABLED
 			physical_bone = NULL;
 			physical_bone = NULL;
 			cache_parent_physical_bone = NULL;
 			cache_parent_physical_bone = NULL;
@@ -86,17 +111,12 @@ class Skeleton : public Spatial {
 		}
 		}
 	};
 	};
 
 
-	bool rest_global_inverse_dirty;
-
 	Vector<Bone> bones;
 	Vector<Bone> bones;
 	Vector<int> process_order;
 	Vector<int> process_order;
 	bool process_order_dirty;
 	bool process_order_dirty;
 
 
-	RID skeleton;
-
 	void _make_dirty();
 	void _make_dirty();
 	bool dirty;
 	bool dirty;
-	bool use_bones_in_world_transform;
 
 
 	// bind helpers
 	// bind helpers
 	Array _get_bound_child_nodes_to_bone(int p_bone) const {
 	Array _get_bound_child_nodes_to_bone(int p_bone) const {
@@ -127,8 +147,6 @@ public:
 		NOTIFICATION_UPDATE_SKELETON = 50
 		NOTIFICATION_UPDATE_SKELETON = 50
 	};
 	};
 
 
-	RID get_skeleton() const;
-
 	// skeleton creation api
 	// skeleton creation api
 	void add_bone(const String &p_name);
 	void add_bone(const String &p_name);
 	int find_bone(const String &p_name) const;
 	int find_bone(const String &p_name) const;
@@ -141,9 +159,6 @@ public:
 
 
 	void unparent_bone_and_rest(int p_bone);
 	void unparent_bone_and_rest(int p_bone);
 
 
-	void set_bone_ignore_animation(int p_bone, bool p_ignore);
-	bool is_bone_ignore_animation(int p_bone) const;
-
 	void set_bone_disable_rest(int p_bone, bool p_disable);
 	void set_bone_disable_rest(int p_bone, bool p_disable);
 	bool is_bone_rest_disabled(int p_bone) const;
 	bool is_bone_rest_disabled(int p_bone) const;
 
 
@@ -151,10 +166,9 @@ public:
 
 
 	void set_bone_rest(int p_bone, const Transform &p_rest);
 	void set_bone_rest(int p_bone, const Transform &p_rest);
 	Transform get_bone_rest(int p_bone) const;
 	Transform get_bone_rest(int p_bone) const;
-	Transform get_bone_transform(int p_bone) const;
 	Transform get_bone_global_pose(int p_bone) const;
 	Transform get_bone_global_pose(int p_bone) const;
 
 
-	void set_bone_global_pose(int p_bone, const Transform &p_pose);
+	void set_bone_global_pose_override(int p_bone, const Transform &p_pose, float p_amount, bool p_persistent = false);
 
 
 	void set_bone_enabled(int p_bone, bool p_enabled);
 	void set_bone_enabled(int p_bone, bool p_enabled);
 	bool is_bone_enabled(int p_bone) const;
 	bool is_bone_enabled(int p_bone) const;
@@ -170,14 +184,10 @@ public:
 	void set_bone_pose(int p_bone, const Transform &p_pose);
 	void set_bone_pose(int p_bone, const Transform &p_pose);
 	Transform get_bone_pose(int p_bone) const;
 	Transform get_bone_pose(int p_bone) const;
 
 
-	void set_bone_custom_pose(int p_bone, const Transform &p_custom_pose);
-	Transform get_bone_custom_pose(int p_bone) const;
-
 	void localize_rests(); // used for loaders and tools
 	void localize_rests(); // used for loaders and tools
 	int get_process_order(int p_idx);
 	int get_process_order(int p_idx);
 
 
-	void set_use_bones_in_world_transform(bool p_enable);
-	bool is_using_bones_in_world_transform() const;
+	Ref<SkinReference> register_skin(const Ref<Skin> &p_skin);
 
 
 #ifndef _3D_DISABLED
 #ifndef _3D_DISABLED
 	// Physical bone API
 	// Physical bone API

+ 1 - 1
scene/animation/animation_player.cpp

@@ -256,7 +256,7 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim) {
 
 
 			Skeleton *sk = Object::cast_to<Skeleton>(child);
 			Skeleton *sk = Object::cast_to<Skeleton>(child);
 			bone_idx = sk->find_bone(a->track_get_path(i).get_subname(0));
 			bone_idx = sk->find_bone(a->track_get_path(i).get_subname(0));
-			if (bone_idx == -1 || sk->is_bone_ignore_animation(bone_idx)) {
+			if (bone_idx == -1) {
 
 
 				continue;
 				continue;
 			}
 			}

+ 1 - 1
scene/animation/animation_tree.cpp

@@ -622,7 +622,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
 
 
 							Skeleton *sk = Object::cast_to<Skeleton>(spatial);
 							Skeleton *sk = Object::cast_to<Skeleton>(spatial);
 							int bone_idx = sk->find_bone(path.get_subname(0));
 							int bone_idx = sk->find_bone(path.get_subname(0));
-							if (bone_idx != -1 && !sk->is_bone_ignore_animation(bone_idx)) {
+							if (bone_idx != -1) {
 
 
 								track_xform->skeleton = sk;
 								track_xform->skeleton = sk;
 								track_xform->bone_idx = bone_idx;
 								track_xform->bone_idx = bone_idx;

+ 1 - 5
scene/animation/animation_tree_player.cpp

@@ -820,11 +820,7 @@ void AnimationTreePlayer::_process_animation(float p_delta) {
 		t.value = t.object->get_indexed(t.subpath);
 		t.value = t.object->get_indexed(t.subpath);
 		t.value.zero();
 		t.value.zero();
 
 
-		if (t.skeleton) {
-			t.skip = t.skeleton->is_bone_ignore_animation(t.bone_idx);
-		} else {
-			t.skip = false;
-		}
+		t.skip = false;
 	}
 	}
 
 
 	/* STEP 2 PROCESS ANIMATIONS */
 	/* STEP 2 PROCESS ANIMATIONS */

+ 1 - 1
scene/animation/skeleton_ik.cpp

@@ -320,7 +320,7 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove
 				new_bone_pose.basis = new_bone_pose.basis * p_task->chain.tips[0].end_effector->goal_transform.basis;
 				new_bone_pose.basis = 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);
+		p_task->skeleton->set_bone_global_pose_override(ci->bone, new_bone_pose, 1.0);
 
 
 		if (!ci->children.empty())
 		if (!ci->children.empty())
 			ci = &ci->children.write[0];
 			ci = &ci->children.write[0];

+ 3 - 0
scene/register_scene_types.cpp

@@ -364,6 +364,9 @@ void register_scene_types() {
 
 
 	/* REGISTER 3D */
 	/* REGISTER 3D */
 
 
+	ClassDB::register_class<Skin>();
+	ClassDB::register_virtual_class<SkinReference>();
+
 	ClassDB::register_class<Spatial>();
 	ClassDB::register_class<Spatial>();
 	ClassDB::register_virtual_class<SpatialGizmo>();
 	ClassDB::register_virtual_class<SpatialGizmo>();
 	ClassDB::register_class<Skeleton>();
 	ClassDB::register_class<Skeleton>();

+ 102 - 0
scene/resources/skin.cpp

@@ -0,0 +1,102 @@
+#include "skin.h"
+
+void Skin::set_bind_count(int p_size) {
+	ERR_FAIL_COND(p_size < 0);
+	binds.resize(p_size);
+	binds_ptr = binds.ptrw();
+	bind_count = p_size;
+	emit_changed();
+}
+
+void Skin::add_bind(int p_bone, const Transform &p_pose) {
+	uint32_t index = bind_count;
+	set_bind_count(bind_count + 1);
+	set_bind_bone(index, p_bone);
+	set_bind_pose(index, p_pose);
+}
+
+void Skin::set_bind_bone(int p_index, int p_bone) {
+	ERR_FAIL_INDEX(p_index, bind_count);
+	binds_ptr[p_index].bone = p_bone;
+	emit_changed();
+}
+
+void Skin::set_bind_pose(int p_index, const Transform &p_pose) {
+	ERR_FAIL_INDEX(p_index, bind_count);
+	binds_ptr[p_index].pose = p_pose;
+	emit_changed();
+}
+
+void Skin::clear_binds() {
+	binds.clear();
+	binds_ptr = nullptr;
+	bind_count = 0;
+	emit_changed();
+}
+
+bool Skin::_set(const StringName &p_name, const Variant &p_value) {
+	String name = p_name;
+	if (name == "bind_count") {
+		set_bind_count(p_value);
+		return true;
+	} else if (name.begins_with("bind/")) {
+		int index = name.get_slicec('/', 1).to_int();
+		String what = name.get_slicec('/', 2);
+		if (what == "bone") {
+			set_bind_bone(index, p_value);
+			return true;
+		} else if (what == "pose") {
+			set_bind_pose(index, p_value);
+			return true;
+		}
+	}
+	return false;
+}
+
+bool Skin::_get(const StringName &p_name, Variant &r_ret) const {
+
+	String name = p_name;
+	if (name == "bind_count") {
+		r_ret = get_bind_count();
+		return true;
+	} else if (name.begins_with("bind/")) {
+		int index = name.get_slicec('/', 1).to_int();
+		String what = name.get_slicec('/', 2);
+		if (what == "bone") {
+			r_ret = get_bind_bone(index);
+			return true;
+		} else if (what == "pose") {
+			r_ret = get_bind_pose(index);
+			return true;
+		}
+	}
+	return false;
+}
+void Skin::_get_property_list(List<PropertyInfo> *p_list) const {
+	p_list->push_back(PropertyInfo(Variant::INT, "bind_count", PROPERTY_HINT_RANGE, "0,16384,1,or_greater"));
+	for (int i = 0; i < get_bind_count(); i++) {
+		p_list->push_back(PropertyInfo(Variant::INT, "bind/" + itos(i) + "/bone", PROPERTY_HINT_RANGE, "0,16384,1,or_greater"));
+		p_list->push_back(PropertyInfo(Variant::TRANSFORM, "bind/" + itos(i) + "/pose"));
+	}
+}
+
+void Skin::_bind_methods() {
+
+	ClassDB::bind_method(D_METHOD("set_bind_count", "bind_count"), &Skin::set_bind_count);
+	ClassDB::bind_method(D_METHOD("get_bind_count"), &Skin::get_bind_count);
+
+	ClassDB::bind_method(D_METHOD("add_bind", "bone", "pose"), &Skin::add_bind);
+
+	ClassDB::bind_method(D_METHOD("set_bind_pose", "bind_index", "pose"), &Skin::set_bind_pose);
+	ClassDB::bind_method(D_METHOD("get_bind_pose", "bind_index"), &Skin::get_bind_pose);
+
+	ClassDB::bind_method(D_METHOD("set_bind_bone", "bind_index", "bone"), &Skin::set_bind_bone);
+	ClassDB::bind_method(D_METHOD("get_bind_bone", "bind_index"), &Skin::get_bind_bone);
+
+	ClassDB::bind_method(D_METHOD("clear_binds"), &Skin::clear_binds);
+}
+
+Skin::Skin() {
+	bind_count = 0;
+	binds_ptr = nullptr;
+}

+ 54 - 0
scene/resources/skin.h

@@ -0,0 +1,54 @@
+#ifndef SKIN_H
+#define SKIN_H
+
+#include "core/resource.h"
+
+class Skin : public Resource {
+	GDCLASS(Skin, Resource)
+
+	struct Bind {
+		int bone;
+		Transform pose;
+	};
+
+	Vector<Bind> binds;
+
+	Bind *binds_ptr;
+	int bind_count;
+
+protected:
+	bool _set(const StringName &p_name, const Variant &p_value);
+	bool _get(const StringName &p_name, Variant &r_ret) const;
+	void _get_property_list(List<PropertyInfo> *p_list) const;
+
+	static void _bind_methods();
+
+public:
+	void set_bind_count(int p_size);
+	inline int get_bind_count() const { return bind_count; }
+
+	void add_bind(int p_bone, const Transform &p_pose);
+
+	void set_bind_bone(int p_index, int p_bone);
+	void set_bind_pose(int p_index, const Transform &p_pose);
+
+	inline int get_bind_bone(int p_index) const {
+#ifdef DEBUG_ENABLED
+		ERR_FAIL_INDEX_V(p_index, bind_count, -1);
+#endif
+		return binds_ptr[p_index].bone;
+	}
+
+	inline Transform get_bind_pose(int p_index) const {
+#ifdef DEBUG_ENABLED
+		ERR_FAIL_INDEX_V(p_index, bind_count, Transform());
+#endif
+		return binds_ptr[p_index].pose;
+	}
+
+	void clear_binds();
+
+	Skin();
+};
+
+#endif // SKIN_H

+ 0 - 1
servers/visual/rasterizer.h

@@ -357,7 +357,6 @@ public:
 	virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) = 0;
 	virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) = 0;
 	virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const = 0;
 	virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const = 0;
 	virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) = 0;
 	virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) = 0;
-	virtual void skeleton_set_world_transform(RID p_skeleton, bool p_enable, const Transform &p_world_transform) = 0;
 
 
 	/* Light API */
 	/* Light API */
 
 

+ 0 - 1
servers/visual/visual_server_raster.h

@@ -294,7 +294,6 @@ public:
 	BIND3(skeleton_bone_set_transform_2d, RID, int, const Transform2D &)
 	BIND3(skeleton_bone_set_transform_2d, RID, int, const Transform2D &)
 	BIND2RC(Transform2D, skeleton_bone_get_transform_2d, RID, int)
 	BIND2RC(Transform2D, skeleton_bone_get_transform_2d, RID, int)
 	BIND2(skeleton_set_base_transform_2d, RID, const Transform2D &)
 	BIND2(skeleton_set_base_transform_2d, RID, const Transform2D &)
-	BIND3(skeleton_set_world_transform, RID, bool, const Transform &)
 
 
 	/* Light API */
 	/* Light API */
 
 

+ 0 - 1
servers/visual/visual_server_wrap_mt.h

@@ -230,7 +230,6 @@ public:
 	FUNC3(skeleton_bone_set_transform_2d, RID, int, const Transform2D &)
 	FUNC3(skeleton_bone_set_transform_2d, RID, int, const Transform2D &)
 	FUNC2RC(Transform2D, skeleton_bone_get_transform_2d, RID, int)
 	FUNC2RC(Transform2D, skeleton_bone_get_transform_2d, RID, int)
 	FUNC2(skeleton_set_base_transform_2d, RID, const Transform2D &)
 	FUNC2(skeleton_set_base_transform_2d, RID, const Transform2D &)
-	FUNC3(skeleton_set_world_transform, RID, bool, const Transform &)
 
 
 	/* Light API */
 	/* Light API */
 
 

+ 0 - 1
servers/visual_server.h

@@ -391,7 +391,6 @@ public:
 	virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) = 0;
 	virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) = 0;
 	virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const = 0;
 	virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const = 0;
 	virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) = 0;
 	virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) = 0;
-	virtual void skeleton_set_world_transform(RID p_skeleton, bool p_enable, const Transform &p_base_transform) = 0;
 
 
 	/* Light API */
 	/* Light API */
 
 

Some files were not shown because too many files changed in this diff