소스 검색

2D Skeletons WORK IN PROGRESS

Juan Linietsky 7 년 전
부모
커밋
7cd867c3fe

+ 1 - 1
drivers/dummy/rasterizer_dummy.h

@@ -619,7 +619,7 @@ public:
 	void canvas_begin(){};
 	void canvas_end(){};
 
-	void canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light){};
+	void canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light,const Transform2D& p_transform){};
 	void canvas_debug_viewport_shadows(Light *p_lights_with_shadow){};
 
 	void canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, CameraMatrix *p_xform_cache) {}

+ 1 - 1
drivers/gles3/rasterizer_canvas_gles3.cpp

@@ -1042,7 +1042,7 @@ void RasterizerCanvasGLES3::_copy_texscreen(const Rect2 &p_rect) {
 	glEnable(GL_BLEND);
 }
 
-void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light) {
+void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_transform) {
 
 	Item *current_clip = NULL;
 	RasterizerStorageGLES3::Shader *shader_cache = NULL;

+ 1 - 1
drivers/gles3/rasterizer_canvas_gles3.h

@@ -129,7 +129,7 @@ public:
 	_FORCE_INLINE_ void _canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip);
 	_FORCE_INLINE_ void _copy_texscreen(const Rect2 &p_rect);
 
-	virtual void canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light);
+	virtual void canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_transform);
 	virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow);
 
 	virtual void canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, CameraMatrix *p_xform_cache);

+ 2 - 0
editor/editor_node.cpp

@@ -99,6 +99,7 @@
 #include "editor/plugins/script_text_editor.h"
 #include "editor/plugins/shader_editor_plugin.h"
 #include "editor/plugins/shader_graph_editor_plugin.h"
+#include "editor/plugins/skeleton_2d_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"
@@ -5673,6 +5674,7 @@ EditorNode::EditorNode() {
 	add_editor_plugin(memnew(MeshLibraryEditorPlugin(this)));
 	add_editor_plugin(memnew(StyleBoxEditorPlugin(this)));
 	add_editor_plugin(memnew(SpriteEditorPlugin(this)));
+	add_editor_plugin(memnew(Skeleton2DEditorPlugin(this)));
 	add_editor_plugin(memnew(ParticlesEditorPlugin(this)));
 	add_editor_plugin(memnew(ResourcePreloaderEditorPlugin(this)));
 	add_editor_plugin(memnew(ItemListEditorPlugin(this)));

+ 115 - 0
editor/plugins/skeleton_2d_editor_plugin.cpp

@@ -0,0 +1,115 @@
+#include "skeleton_2d_editor_plugin.h"
+
+#include "canvas_item_editor_plugin.h"
+#include "scene/2d/mesh_instance_2d.h"
+#include "scene/gui/box_container.h"
+#include "thirdparty/misc/clipper.hpp"
+
+void Skeleton2DEditor::_node_removed(Node *p_node) {
+
+	if (p_node == node) {
+		node = NULL;
+		options->hide();
+	}
+}
+
+void Skeleton2DEditor::edit(Skeleton2D *p_sprite) {
+
+	node = p_sprite;
+}
+
+void Skeleton2DEditor::_menu_option(int p_option) {
+
+	switch (p_option) {
+		case MENU_OPTION_MAKE_REST: {
+
+			if (node->get_bone_count() == 0) {
+				err_dialog->set_text(TTR("This skeleton has no bones, create some children Bone2D nodes."));
+				err_dialog->popup_centered_minsize();
+				return;
+			}
+			UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+			ur->create_action("Create Rest Pose from Bones");
+			for (int i = 0; i < node->get_bone_count(); i++) {
+				Bone2D *bone = node->get_bone(i);
+				ur->add_do_method(bone, "set_rest", bone->get_transform());
+				ur->add_undo_method(bone, "set_rest", bone->get_rest());
+			}
+			ur->commit_action();
+
+		} break;
+		case MENU_OPTION_SET_REST: {
+			if (node->get_bone_count() == 0) {
+				err_dialog->set_text(TTR("This skeleton has no bones, create some children Bone2D nodes."));
+				err_dialog->popup_centered_minsize();
+				return;
+			}
+			UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+			ur->create_action("Set Rest Pose to Bones");
+			for (int i = 0; i < node->get_bone_count(); i++) {
+				Bone2D *bone = node->get_bone(i);
+				ur->add_do_method(bone, "set_transform", bone->get_rest());
+				ur->add_undo_method(bone, "set_transform", bone->get_transform());
+			}
+			ur->commit_action();
+
+		} break;
+	}
+}
+
+void Skeleton2DEditor::_bind_methods() {
+
+	ClassDB::bind_method("_menu_option", &Skeleton2DEditor::_menu_option);
+}
+
+Skeleton2DEditor::Skeleton2DEditor() {
+
+	options = memnew(MenuButton);
+
+	CanvasItemEditor::get_singleton()->add_control_to_menu_panel(options);
+
+	options->set_text(TTR("Skeleton2D"));
+	options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Skeleton2D", "EditorIcons"));
+
+	options->get_popup()->add_item(TTR("Make Rest Pose (From Bones)"), MENU_OPTION_MAKE_REST);
+	options->get_popup()->add_separator();
+	options->get_popup()->add_item(TTR("Set Bones to Rest Pose"), MENU_OPTION_SET_REST);
+
+	options->get_popup()->connect("id_pressed", this, "_menu_option");
+
+	err_dialog = memnew(AcceptDialog);
+	add_child(err_dialog);
+}
+
+void Skeleton2DEditorPlugin::edit(Object *p_object) {
+
+	sprite_editor->edit(Object::cast_to<Skeleton2D>(p_object));
+}
+
+bool Skeleton2DEditorPlugin::handles(Object *p_object) const {
+
+	return p_object->is_class("Skeleton2D");
+}
+
+void Skeleton2DEditorPlugin::make_visible(bool p_visible) {
+
+	if (p_visible) {
+		sprite_editor->options->show();
+	} else {
+
+		sprite_editor->options->hide();
+		sprite_editor->edit(NULL);
+	}
+}
+
+Skeleton2DEditorPlugin::Skeleton2DEditorPlugin(EditorNode *p_node) {
+
+	editor = p_node;
+	sprite_editor = memnew(Skeleton2DEditor);
+	editor->get_viewport()->add_child(sprite_editor);
+
+	//sprite_editor->options->hide();
+}
+
+Skeleton2DEditorPlugin::~Skeleton2DEditorPlugin() {
+}

+ 55 - 0
editor/plugins/skeleton_2d_editor_plugin.h

@@ -0,0 +1,55 @@
+#ifndef SKELETON_2D_EDITOR_PLUGIN_H
+#define SKELETON_2D_EDITOR_PLUGIN_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "scene/2d/skeleton_2d.h"
+#include "scene/gui/spin_box.h"
+
+class Skeleton2DEditor : public Control {
+
+	GDCLASS(Skeleton2DEditor, Control);
+
+	enum Menu {
+		MENU_OPTION_MAKE_REST,
+		MENU_OPTION_SET_REST,
+	};
+
+	Skeleton2D *node;
+
+	MenuButton *options;
+	AcceptDialog *err_dialog;
+
+	void _menu_option(int p_option);
+
+	//void _create_uv_lines();
+	friend class Skeleton2DEditorPlugin;
+
+protected:
+	void _node_removed(Node *p_node);
+	static void _bind_methods();
+
+public:
+	void edit(Skeleton2D *p_sprite);
+	Skeleton2DEditor();
+};
+
+class Skeleton2DEditorPlugin : public EditorPlugin {
+
+	GDCLASS(Skeleton2DEditorPlugin, EditorPlugin);
+
+	Skeleton2DEditor *sprite_editor;
+	EditorNode *editor;
+
+public:
+	virtual String get_name() const { return "Skeleton2D"; }
+	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);
+
+	Skeleton2DEditorPlugin(EditorNode *p_node);
+	~Skeleton2DEditorPlugin();
+};
+
+#endif // SKELETON_2D_EDITOR_PLUGIN_H

+ 3 - 3
scene/2d/canvas_item.cpp

@@ -779,13 +779,13 @@ void CanvasItem::draw_colored_polygon(const Vector<Point2> &p_points, const Colo
 	VisualServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, colors, p_uvs, rid, rid_normal, p_antialiased);
 }
 
-void CanvasItem::draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map, RID p_skeleton) {
+void CanvasItem::draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map) {
 
 	ERR_FAIL_COND(p_mesh.is_null());
 	RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
 	RID normal_map_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
 
-	VisualServer::get_singleton()->canvas_item_add_mesh(canvas_item, p_mesh->get_rid(), texture_rid, normal_map_rid, p_skeleton);
+	VisualServer::get_singleton()->canvas_item_add_mesh(canvas_item, p_mesh->get_rid(), texture_rid, normal_map_rid);
 }
 void CanvasItem::draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map) {
 
@@ -1032,7 +1032,7 @@ void CanvasItem::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("draw_colored_polygon", "points", "color", "uvs", "texture", "normal_map", "antialiased"), &CanvasItem::draw_colored_polygon, DEFVAL(PoolVector2Array()), DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(false));
 	ClassDB::bind_method(D_METHOD("draw_string", "font", "position", "text", "modulate", "clip_w"), &CanvasItem::draw_string, DEFVAL(Color(1, 1, 1)), DEFVAL(-1));
 	ClassDB::bind_method(D_METHOD("draw_char", "font", "position", "char", "next", "modulate"), &CanvasItem::draw_char, DEFVAL(Color(1, 1, 1)));
-	ClassDB::bind_method(D_METHOD("draw_mesh", "mesh", "texture", "normal_map", "skeleton"), &CanvasItem::draw_mesh, DEFVAL(Ref<Texture>()), DEFVAL(RID()));
+	ClassDB::bind_method(D_METHOD("draw_mesh", "mesh", "texture", "normal_map"), &CanvasItem::draw_mesh, DEFVAL(Ref<Texture>()));
 	ClassDB::bind_method(D_METHOD("draw_multimesh", "mesh", "texture", "normal_map"), &CanvasItem::draw_mesh, DEFVAL(Ref<Texture>()));
 
 	ClassDB::bind_method(D_METHOD("draw_set_transform", "position", "rotation", "scale"), &CanvasItem::draw_set_transform);

+ 1 - 1
scene/2d/canvas_item.h

@@ -283,7 +283,7 @@ public:
 	void draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture> p_texture = Ref<Texture>(), const Ref<Texture> &p_normal_map = Ref<Texture>(), bool p_antialiased = false);
 	void draw_colored_polygon(const Vector<Point2> &p_points, const Color &p_color, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture> p_texture = Ref<Texture>(), const Ref<Texture> &p_normal_map = Ref<Texture>(), bool p_antialiased = false);
 
-	void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map, RID p_skeleton);
+	void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map);
 	void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map);
 
 	void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1);

+ 1 - 1
scene/2d/mesh_instance_2d.cpp

@@ -4,7 +4,7 @@ void MeshInstance2D::_notification(int p_what) {
 
 	if (p_what == NOTIFICATION_DRAW) {
 		if (mesh.is_valid()) {
-			draw_mesh(mesh, texture, normal_map, RID());
+			draw_mesh(mesh, texture, normal_map);
 		}
 	}
 }

+ 210 - 0
scene/2d/skeleton_2d.cpp

@@ -0,0 +1,210 @@
+#include "skeleton_2d.h"
+
+void Bone2D::_notification(int p_what) {
+
+	if (p_what == NOTIFICATION_ENTER_TREE) {
+		Node *parent = get_parent();
+		parent_bone = Object::cast_to<Bone2D>(parent);
+		skeleton = NULL;
+		while (parent) {
+			skeleton = Object::cast_to<Skeleton2D>(parent);
+			if (skeleton)
+				break;
+			if (!Object::cast_to<Bone2D>(parent))
+				break; //skeletons must be chained to Bone2Ds.
+		}
+
+		if (skeleton) {
+			Skeleton2D::Bone bone;
+			bone.bone = this;
+			skeleton->bones.push_back(bone);
+			skeleton->_make_bone_setup_dirty();
+		}
+	}
+	if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
+		if (skeleton) {
+			skeleton->_make_transform_dirty();
+		}
+	}
+
+	if (p_what == NOTIFICATION_EXIT_TREE) {
+		if (skeleton) {
+			for (int i = 0; i < skeleton->bones.size(); i++) {
+				if (skeleton->bones[i].bone == this) {
+					skeleton->bones.remove(i);
+					break;
+				}
+			}
+			skeleton->_make_bone_setup_dirty();
+			skeleton = NULL;
+		}
+		parent_bone = NULL;
+	}
+}
+void Bone2D::_bind_methods() {
+
+	ClassDB::bind_method(D_METHOD("set_rest", "rest"), &Bone2D::set_rest);
+	ClassDB::bind_method(D_METHOD("get_rest"), &Bone2D::get_rest);
+	ClassDB::bind_method(D_METHOD("apply_rest"), &Bone2D::apply_rest);
+}
+
+void Bone2D::set_rest(const Transform2D &p_rest) {
+	rest = p_rest;
+	if (skeleton)
+		skeleton->_make_bone_setup_dirty();
+}
+
+Transform2D Bone2D::get_rest() const {
+	return rest;
+}
+
+Transform2D Bone2D::get_skeleton_rest() const {
+
+	if (parent_bone) {
+		return parent_bone->get_skeleton_rest() * rest;
+	} else {
+		return rest;
+	}
+}
+
+void Bone2D::apply_rest() {
+	set_transform(rest);
+}
+
+String Bone2D::get_configuration_warning() const {
+	if (!skeleton) {
+		if (parent_bone) {
+			return TTR("This Bone2D chain should end at a Skeleton2D node.");
+		} else {
+			return TTR("A Bone2D only works with a Skeleton2D or another Bone2D as parent node.");
+		}
+	}
+
+	return Node2D::get_configuration_warning();
+}
+
+Bone2D::Bone2D() {
+	skeleton = NULL;
+	parent_bone = NULL;
+	set_notify_local_transform(true);
+}
+
+//////////////////////////////////////
+
+void Skeleton2D::_make_bone_setup_dirty() {
+
+	if (bone_setup_dirty)
+		return;
+	bone_setup_dirty = true;
+	if (is_inside_tree()) {
+		call_deferred("_update_bone_setup");
+	}
+}
+
+void Skeleton2D::_update_bone_setup() {
+
+	if (!bone_setup_dirty)
+		return;
+
+	bone_setup_dirty = false;
+	VS::get_singleton()->skeleton_allocate(skeleton, bones.size(), true);
+
+	bones.sort(); //sorty so they are always in the same order/index
+
+	for (int i = 0; i < bones.size(); i++) {
+		bones[i].rest_inverse = bones[i].bone->get_skeleton_rest(); //bind pose
+	}
+
+	transform_dirty = true;
+	_update_transform();
+}
+
+void Skeleton2D::_make_transform_dirty() {
+
+	if (transform_dirty)
+		return;
+	transform_dirty = true;
+	if (is_inside_tree()) {
+		call_deferred("_update_transform");
+	}
+}
+
+void Skeleton2D::_update_transform() {
+
+	if (bone_setup_dirty) {
+		_update_bone_setup();
+		return; //above will update transform anyway
+	}
+	if (!transform_dirty)
+		return;
+
+	transform_dirty = false;
+
+	Transform2D global_xform = get_global_transform();
+	Transform2D global_xform_inverse = global_xform.affine_inverse();
+
+	for (int i = 0; i < bones.size(); i++) {
+
+		Transform2D final_xform = bones[i].rest_inverse * bones[i].bone->get_relative_transform_to_parent(this);
+		VS::get_singleton()->skeleton_bone_set_transform_2d(skeleton, i, global_xform * (final_xform * global_xform_inverse));
+	}
+}
+
+int Skeleton2D::get_bone_count() const {
+
+	ERR_FAIL_COND_V(!is_inside_tree(), 0);
+
+	if (bone_setup_dirty) {
+		const_cast<Skeleton2D *>(this)->_update_bone_setup();
+	}
+
+	return bones.size();
+}
+
+Bone2D *Skeleton2D::get_bone(int p_idx) {
+
+	ERR_FAIL_COND_V(!is_inside_tree(), NULL);
+	ERR_FAIL_INDEX_V(p_idx, bones.size(), NULL);
+
+	return bones[p_idx].bone;
+}
+
+void Skeleton2D::_notification(int p_what) {
+
+	if (p_what == NOTIFICATION_READY) {
+
+		if (bone_setup_dirty)
+			_update_bone_setup();
+		if (transform_dirty)
+			_update_transform();
+	}
+
+	if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
+		_make_transform_dirty();
+	}
+}
+
+RID Skeleton2D::get_skeleton() const {
+	return skeleton;
+}
+void Skeleton2D::_bind_methods() {
+
+	ClassDB::bind_method(D_METHOD("_update_bone_setup"), &Skeleton2D::_update_bone_setup);
+	ClassDB::bind_method(D_METHOD("_update_transform"), &Skeleton2D::_update_transform);
+
+	ClassDB::bind_method(D_METHOD("get_bone_count"), &Skeleton2D::get_bone_count);
+	ClassDB::bind_method(D_METHOD("get_bone"), &Skeleton2D::get_bone);
+
+	ClassDB::bind_method(D_METHOD("get_skeleton"), &Skeleton2D::get_skeleton);
+}
+
+Skeleton2D::Skeleton2D() {
+	bone_setup_dirty = true;
+	transform_dirty = true;
+	skeleton = VS::get_singleton()->skeleton_create();
+}
+
+Skeleton2D::~Skeleton2D() {
+
+	VS::get_singleton()->free(skeleton);
+}

+ 68 - 0
scene/2d/skeleton_2d.h

@@ -0,0 +1,68 @@
+#ifndef SKELETON_2D_H
+#define SKELETON_2D_H
+
+#include "scene/2d/node_2d.h"
+
+class Skeleton2D;
+
+class Bone2D : public Node2D {
+	GDCLASS(Bone2D, Node2D)
+
+	Bone2D *parent_bone;
+	Skeleton2D *skeleton;
+	Transform2D rest;
+
+protected:
+	void _notification(int p_what);
+	static void _bind_methods();
+
+public:
+	void set_rest(const Transform2D &p_rest);
+	Transform2D get_rest() const;
+	void apply_rest();
+	Transform2D get_skeleton_rest() const;
+
+	String get_configuration_warning() const;
+
+	Bone2D();
+};
+
+class Skeleton2D : public Node2D {
+	GDCLASS(Skeleton2D, Node2D);
+
+	friend class Bone2D;
+
+	struct Bone {
+		bool operator<(const Bone &p_bone) const {
+			p_bone.bone->is_greater_than(bone);
+		}
+		Bone2D *bone;
+		Transform2D rest_inverse;
+	};
+
+	Vector<Bone> bones;
+
+	bool bone_setup_dirty;
+	void _make_bone_setup_dirty();
+	void _update_bone_setup();
+
+	bool transform_dirty;
+	void _make_transform_dirty();
+	void _update_transform();
+
+	RID skeleton;
+
+protected:
+	void _notification(int p_what);
+	static void _bind_methods();
+
+public:
+	int get_bone_count() const;
+	Bone2D *get_bone(int p_idx);
+
+	RID get_skeleton() const;
+	Skeleton2D();
+	~Skeleton2D();
+};
+
+#endif // SKELETON_2D_H

+ 2 - 4
servers/visual/rasterizer.h

@@ -749,7 +749,6 @@ public:
 		struct CommandMesh : public Command {
 
 			RID mesh;
-			RID skeleton;
 			RID texture;
 			RID normal_map;
 			CommandMesh() { type = TYPE_MESH; }
@@ -758,7 +757,6 @@ public:
 		struct CommandMultiMesh : public Command {
 
 			RID multimesh;
-			RID skeleton;
 			RID texture;
 			RID normal_map;
 			CommandMultiMesh() { type = TYPE_MULTIMESH; }
@@ -924,7 +922,7 @@ public:
 					case Item::Command::TYPE_MESH: {
 
 						const Item::CommandMesh *mesh = static_cast<const Item::CommandMesh *>(c);
-						AABB aabb = RasterizerStorage::base_singleton->mesh_get_aabb(mesh->mesh, mesh->skeleton);
+						AABB aabb = RasterizerStorage::base_singleton->mesh_get_aabb(mesh->mesh, RID());
 
 						r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
 
@@ -1016,7 +1014,7 @@ public:
 	virtual void canvas_begin() = 0;
 	virtual void canvas_end() = 0;
 
-	virtual void canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light) = 0;
+	virtual void canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform) = 0;
 	virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) = 0;
 
 	struct LightOccluderInstance : public RID_Data {

+ 13 - 6
servers/visual/visual_server_canvas.cpp

@@ -46,7 +46,7 @@ void VisualServerCanvas::_render_canvas_item_tree(Item *p_canvas_item, const Tra
 	for (int i = 0; i < z_range; i++) {
 		if (!z_list[i])
 			continue;
-		VSG::canvas_render->canvas_render_items(z_list[i], VS::CANVAS_ITEM_Z_MIN + i, p_modulate, p_lights);
+		VSG::canvas_render->canvas_render_items(z_list[i], VS::CANVAS_ITEM_Z_MIN + i, p_modulate, p_lights, p_transform);
 	}
 }
 
@@ -214,7 +214,7 @@ void VisualServerCanvas::render_canvas(Canvas *p_canvas, const Transform2D &p_tr
 				_light_mask_canvas_items(VS::CANVAS_ITEM_Z_MIN + i, z_list[i], p_masked_lights);
 			}
 
-			VSG::canvas_render->canvas_render_items(z_list[i], VS::CANVAS_ITEM_Z_MIN + i, p_canvas->modulate, p_lights);
+			VSG::canvas_render->canvas_render_items(z_list[i], VS::CANVAS_ITEM_Z_MIN + i, p_canvas->modulate, p_lights, p_transform);
 		}
 	} else {
 
@@ -742,7 +742,7 @@ void VisualServerCanvas::canvas_item_add_set_transform(RID p_item, const Transfo
 	canvas_item->commands.push_back(tr);
 }
 
-void VisualServerCanvas::canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_texture, RID p_normal_map, RID p_skeleton) {
+void VisualServerCanvas::canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_texture, RID p_normal_map) {
 
 	Item *canvas_item = canvas_item_owner.getornull(p_item);
 	ERR_FAIL_COND(!canvas_item);
@@ -752,7 +752,6 @@ void VisualServerCanvas::canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID
 	m->mesh = p_mesh;
 	m->texture = p_texture;
 	m->normal_map = p_normal_map;
-	m->skeleton = p_skeleton;
 
 	canvas_item->commands.push_back(m);
 }
@@ -776,7 +775,7 @@ void VisualServerCanvas::canvas_item_add_particles(RID p_item, RID p_particles,
 	canvas_item->commands.push_back(part);
 }
 
-void VisualServerCanvas::canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture, RID p_normal_map, RID p_skeleton) {
+void VisualServerCanvas::canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture, RID p_normal_map) {
 
 	Item *canvas_item = canvas_item_owner.getornull(p_item);
 	ERR_FAIL_COND(!canvas_item);
@@ -784,7 +783,6 @@ void VisualServerCanvas::canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p
 	Item::CommandMultiMesh *mm = memnew(Item::CommandMultiMesh);
 	ERR_FAIL_COND(!mm);
 	mm->multimesh = p_mesh;
-	mm->skeleton = p_skeleton;
 	mm->texture = p_texture;
 	mm->normal_map = p_normal_map;
 
@@ -826,6 +824,15 @@ void VisualServerCanvas::canvas_item_set_z_as_relative_to_parent(RID p_item, boo
 
 	canvas_item->z_relative = p_enable;
 }
+
+void VisualServerCanvas::canvas_item_attach_skeleton(RID p_item, RID p_skeleton) {
+
+	Item *canvas_item = canvas_item_owner.getornull(p_item);
+	ERR_FAIL_COND(!canvas_item);
+
+	canvas_item->skeleton = p_skeleton;
+}
+
 void VisualServerCanvas::canvas_item_set_copy_to_backbuffer(RID p_item, bool p_enable, const Rect2 &p_rect) {
 
 	Item *canvas_item = canvas_item_owner.getornull(p_item);

+ 5 - 2
servers/visual/visual_server_canvas.h

@@ -51,6 +51,8 @@ public:
 
 		Vector<Item *> child_items;
 
+		RID skeleton;
+
 		Item() {
 			children_order_dirty = true;
 			E = NULL;
@@ -182,8 +184,8 @@ public:
 	void canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture, float p_width = 1.0, RID p_normal_map = RID());
 	void canvas_item_add_polygon(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID(), RID p_normal_map = RID(), bool p_antialiased = false);
 	void canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID(), int p_count = -1, RID p_normal_map = RID());
-	void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_texture = RID(), RID p_normal_map = RID(), RID p_skeleton = RID());
-	void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture = RID(), RID p_normal_map = RID(), RID p_skeleton = RID());
+	void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_texture = RID(), RID p_normal_map = RID());
+	void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture = RID(), RID p_normal_map = RID());
 	void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal, int p_h_frames, int p_v_frames);
 	void canvas_item_add_set_transform(RID p_item, const Transform2D &p_transform);
 	void canvas_item_add_clip_ignore(RID p_item, bool p_ignore);
@@ -191,6 +193,7 @@ public:
 	void canvas_item_set_z_index(RID p_item, int p_z);
 	void canvas_item_set_z_as_relative_to_parent(RID p_item, bool p_enable);
 	void canvas_item_set_copy_to_backbuffer(RID p_item, bool p_enable, const Rect2 &p_rect);
+	void canvas_item_attach_skeleton(RID p_item, RID p_skeleton);
 
 	void canvas_item_clear(RID p_item);
 	void canvas_item_set_draw_index(RID p_item, int p_index);

+ 3 - 2
servers/visual/visual_server_raster.h

@@ -581,8 +581,8 @@ public:
 	BIND7(canvas_item_add_primitive, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, float, RID)
 	BIND7(canvas_item_add_polygon, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, RID, bool)
 	BIND8(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, int, RID)
-	BIND5(canvas_item_add_mesh, RID, const RID &, RID, RID, RID)
-	BIND5(canvas_item_add_multimesh, RID, RID, RID, RID, RID)
+	BIND4(canvas_item_add_mesh, RID, const RID &, RID, RID)
+	BIND4(canvas_item_add_multimesh, RID, RID, RID, RID)
 	BIND6(canvas_item_add_particles, RID, RID, RID, RID, int, int)
 	BIND2(canvas_item_add_set_transform, RID, const Transform2D &)
 	BIND2(canvas_item_add_clip_ignore, RID, bool)
@@ -590,6 +590,7 @@ public:
 	BIND2(canvas_item_set_z_index, RID, int)
 	BIND2(canvas_item_set_z_as_relative_to_parent, RID, bool)
 	BIND3(canvas_item_set_copy_to_backbuffer, RID, bool, const Rect2 &)
+	BIND2(canvas_item_attach_skeleton, RID, RID)
 
 	BIND1(canvas_item_clear, RID)
 	BIND2(canvas_item_set_draw_index, RID, int)

+ 3 - 2
servers/visual/visual_server_wrap_mt.h

@@ -499,8 +499,8 @@ public:
 	FUNC7(canvas_item_add_primitive, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, float, RID)
 	FUNC7(canvas_item_add_polygon, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, RID, bool)
 	FUNC8(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, int, RID)
-	FUNC5(canvas_item_add_mesh, RID, const RID &, RID, RID, RID)
-	FUNC5(canvas_item_add_multimesh, RID, RID, RID, RID, RID)
+	FUNC4(canvas_item_add_mesh, RID, const RID &, RID, RID)
+	FUNC4(canvas_item_add_multimesh, RID, RID, RID, RID)
 	FUNC6(canvas_item_add_particles, RID, RID, RID, RID, int, int)
 	FUNC2(canvas_item_add_set_transform, RID, const Transform2D &)
 	FUNC2(canvas_item_add_clip_ignore, RID, bool)
@@ -508,6 +508,7 @@ public:
 	FUNC2(canvas_item_set_z_index, RID, int)
 	FUNC2(canvas_item_set_z_as_relative_to_parent, RID, bool)
 	FUNC3(canvas_item_set_copy_to_backbuffer, RID, bool, const Rect2 &)
+	FUNC2(canvas_item_attach_skeleton, RID, RID)
 
 	FUNC1(canvas_item_clear, RID)
 	FUNC2(canvas_item_set_draw_index, RID, int)

+ 4 - 2
servers/visual_server.h

@@ -846,8 +846,8 @@ public:
 	virtual void canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture, float p_width = 1.0, RID p_normal_map = RID()) = 0;
 	virtual void canvas_item_add_polygon(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID(), RID p_normal_map = RID(), bool p_antialiased = false) = 0;
 	virtual void canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID(), int p_count = -1, RID p_normal_map = RID()) = 0;
-	virtual void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_texture = RID(), RID p_normal_map = RID(), RID p_skeleton = RID()) = 0;
-	virtual void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture = RID(), RID p_normal_map = RID(), RID p_skeleton = RID()) = 0;
+	virtual void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_texture = RID(), RID p_normal_map = RID()) = 0;
+	virtual void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture = RID(), RID p_normal_map = RID()) = 0;
 	virtual void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal_map, int p_h_frames, int p_v_frames) = 0;
 	virtual void canvas_item_add_set_transform(RID p_item, const Transform2D &p_transform) = 0;
 	virtual void canvas_item_add_clip_ignore(RID p_item, bool p_ignore) = 0;
@@ -856,6 +856,8 @@ public:
 	virtual void canvas_item_set_z_as_relative_to_parent(RID p_item, bool p_enable) = 0;
 	virtual void canvas_item_set_copy_to_backbuffer(RID p_item, bool p_enable, const Rect2 &p_rect) = 0;
 
+	virtual void canvas_item_attach_skeleton(RID p_item, RID p_skeleton) = 0;
+
 	virtual void canvas_item_clear(RID p_item) = 0;
 	virtual void canvas_item_set_draw_index(RID p_item, int p_index) = 0;