Browse Source

Merge pull request #11973 from AndreaCatania/ragdoll

Ragdoll - Physical bone node
Juan Linietsky 7 years ago
parent
commit
6d46f73ec3

+ 4 - 0
editor/editor_node.cpp

@@ -93,6 +93,7 @@
 #include "editor/plugins/particles_editor_plugin.h"
 #include "editor/plugins/path_2d_editor_plugin.h"
 #include "editor/plugins/path_editor_plugin.h"
+#include "editor/plugins/physical_bone_plugin.h"
 #include "editor/plugins/polygon_2d_editor_plugin.h"
 #include "editor/plugins/resource_preloader_editor_plugin.h"
 #include "editor/plugins/script_editor_plugin.h"
@@ -100,6 +101,7 @@
 #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/skeleton_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"
@@ -5881,6 +5883,8 @@ EditorNode::EditorNode() {
 	add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
 	add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
 	add_editor_plugin(memnew(NavigationMeshEditorPlugin(this)));
+	add_editor_plugin(memnew(SkeletonEditorPlugin(this)));
+	add_editor_plugin(memnew(PhysicalBonePlugin(this)));
 
 	// FIXME: Disabled as (according to reduz) users were complaining that it gets in the way
 	// Waiting for PropertyEditor rewrite (planned for 3.1) to be refactored.

+ 77 - 0
editor/icons/icon_physical_bone.svg

@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   version="1.1"
+   viewBox="0 0 16 16"
+   id="svg2"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="icon_physical_bone.svg">
+  <metadata
+     id="metadata12">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs10" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1920"
+     inkscape:window-height="1027"
+     id="namedview8"
+     showgrid="false"
+     inkscape:zoom="16"
+     inkscape:cx="14.674088"
+     inkscape:cy="7.3239349"
+     inkscape:window-x="-8"
+     inkscape:window-y="-8"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg2" />
+  <g
+     id="g4505"
+     transform="translate(-2.5625,-18.4375)">
+    <path
+       inkscape:connector-curvature="0"
+       id="path6-9-8"
+       d="m 13.107422,19.382812 a 2.4664,2.4663 0 0 0 -1.78125,0.720704 2.4664,2.4663 0 0 0 -0.185547,0.21289 L 12.472656,22.75 10.867187,23.353516 7.453125,26.767578 a 2.4664,2.4663 0 0 0 -3.1015625,0.3125 2.4664,2.4663 0 0 0 0,3.488281 2.4664,2.4663 0 0 0 1.3964844,0.695313 2.4664,2.4663 0 0 0 0.6953125,1.396484 2.4664,2.4663 0 0 0 3.4882812,0 2.4664,2.4663 0 0 0 0.3144534,-3.103515 l 3.560547,-3.560547 a 2.4664,2.4663 0 0 0 3.099609,-0.310547 2.4664,2.4663 0 0 0 0,-3.488281 A 2.4664,2.4663 0 0 0 15.509766,21.5 2.4664,2.4663 0 0 0 14.814453,20.103516 2.4664,2.4663 0 0 0 13.107422,19.382812 Z"
+       style="fill:#fc9c9c" />
+    <path
+       sodipodi:nodetypes="cccc"
+       id="rect4140-5-1-4-3-7-9-03"
+       d="m 3.7211033,21.208326 0.9608286,4.82644 1.3962404,-0.524494 z"
+       style="opacity:1;fill:#fc9c9c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.42799997;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       inkscape:connector-curvature="0" />
+    <path
+       sodipodi:nodetypes="cccc"
+       id="rect4140-5-1-4-3-7-9-5-3"
+       d="m 6.4843278,19.465234 0.9608285,4.82644 1.3962404,-0.524494 z"
+       style="opacity:1;fill:#fc9c9c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.42799997;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       inkscape:connector-curvature="0" />
+    <path
+       sodipodi:nodetypes="cccc"
+       id="rect4140-5-1-4-3-7-9-5-3-8"
+       d="m 9.6964655,19.33678 0.7108285,3.51394 1.39624,-0.524494 z"
+       style="opacity:1;fill:#fc9c9c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.42799997;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>

+ 123 - 0
editor/plugins/physical_bone_plugin.cpp

@@ -0,0 +1,123 @@
+/*************************************************************************/
+/*  physical_bone_plugin.cpp                                             */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "physical_bone_plugin.h"
+#include "editor/plugins/spatial_editor_plugin.h"
+#include "scene/3d/physics_body.h"
+
+void PhysicalBoneEditor::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("_on_toggle_button_transform_joint", "is_pressed"), &PhysicalBoneEditor::_on_toggle_button_transform_joint);
+}
+
+void PhysicalBoneEditor::_on_toggle_button_transform_joint(bool p_is_pressed) {
+
+	_set_move_joint();
+}
+
+void PhysicalBoneEditor::_set_move_joint() {
+	if (selected) {
+		selected->_set_gizmo_move_joint(button_transform_joint->is_pressed());
+	}
+}
+
+PhysicalBoneEditor::PhysicalBoneEditor(EditorNode *p_editor) :
+		editor(p_editor),
+		selected(NULL) {
+
+	spatial_editor_hb = memnew(HBoxContainer);
+	spatial_editor_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+	spatial_editor_hb->set_alignment(BoxContainer::ALIGN_BEGIN);
+	SpatialEditor::get_singleton()->add_control_to_menu_panel(spatial_editor_hb);
+
+	spatial_editor_hb->add_child(memnew(VSeparator));
+
+	button_transform_joint = memnew(ToolButton);
+	spatial_editor_hb->add_child(button_transform_joint);
+
+	button_transform_joint->set_text(TTR("Move joint"));
+	button_transform_joint->set_icon(SpatialEditor::get_singleton()->get_icon("PhysicalBone", "EditorIcons"));
+	button_transform_joint->set_toggle_mode(true);
+	button_transform_joint->connect("toggled", this, "_on_toggle_button_transform_joint");
+
+	hide();
+}
+
+PhysicalBoneEditor::~PhysicalBoneEditor() {
+	// TODO the spatial_editor_hb should be removed from SpatialEditor, but in this moment it's not possible
+	for (int i = spatial_editor_hb->get_child_count() - 1; 0 <= i; --i) {
+		Node *n = spatial_editor_hb->get_child(i);
+		spatial_editor_hb->remove_child(n);
+		memdelete(n);
+	}
+	memdelete(spatial_editor_hb);
+}
+
+void PhysicalBoneEditor::set_selected(PhysicalBone *p_pb) {
+
+	button_transform_joint->set_pressed(false);
+
+	_set_move_joint();
+	selected = p_pb;
+	_set_move_joint();
+}
+
+void PhysicalBoneEditor::hide() {
+	spatial_editor_hb->hide();
+}
+
+void PhysicalBoneEditor::show() {
+	spatial_editor_hb->show();
+}
+
+PhysicalBonePlugin::PhysicalBonePlugin(EditorNode *p_editor) :
+		editor(p_editor),
+		selected(NULL) {
+
+	physical_bone_editor = memnew(PhysicalBoneEditor(editor));
+}
+
+void PhysicalBonePlugin::make_visible(bool p_visible) {
+	if (p_visible) {
+
+		physical_bone_editor->show();
+	} else {
+
+		physical_bone_editor->hide();
+		physical_bone_editor->set_selected(NULL);
+		selected = NULL;
+	}
+}
+
+void PhysicalBonePlugin::edit(Object *p_node) {
+	selected = static_cast<PhysicalBone *>(p_node); // Trust it
+	ERR_FAIL_COND(!selected);
+
+	physical_bone_editor->set_selected(selected);
+}

+ 78 - 0
editor/plugins/physical_bone_plugin.h

@@ -0,0 +1,78 @@
+/*************************************************************************/
+/*  physical_bone_plugin.h                                               */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef PHYSICAL_BONE_PLUGIN_H
+#define PHYSICAL_BONE_PLUGIN_H
+
+#include "editor/editor_node.h"
+
+class PhysicalBoneEditor : public Object {
+	GDCLASS(PhysicalBoneEditor, Object);
+
+	EditorNode *editor;
+	HBoxContainer *spatial_editor_hb;
+	ToolButton *button_transform_joint;
+
+	PhysicalBone *selected;
+
+protected:
+	static void _bind_methods();
+
+private:
+	void _on_toggle_button_transform_joint(bool p_is_pressed);
+	void _set_move_joint();
+
+public:
+	PhysicalBoneEditor(EditorNode *p_editor);
+	~PhysicalBoneEditor();
+
+	void set_selected(PhysicalBone *p_pb);
+
+	void hide();
+	void show();
+};
+
+class PhysicalBonePlugin : public EditorPlugin {
+	GDCLASS(PhysicalBonePlugin, EditorPlugin);
+
+	EditorNode *editor;
+	PhysicalBone *selected;
+	PhysicalBoneEditor *physical_bone_editor;
+
+public:
+	virtual String get_name() const { return "PhysicalBone"; }
+	virtual bool handles(Object *p_object) const { return p_object->is_class("PhysicalBone"); }
+	virtual void make_visible(bool p_visible);
+	virtual void edit(Object *p_node);
+
+	PhysicalBonePlugin(EditorNode *p_editor);
+};
+
+#endif

+ 189 - 0
editor/plugins/skeleton_editor_plugin.cpp

@@ -0,0 +1,189 @@
+/*************************************************************************/
+/*  skeleton_editor_plugin.cpp                                           */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "skeleton_editor_plugin.h"
+#include "scene/3d/collision_shape.h"
+#include "scene/3d/physics_body.h"
+#include "scene/3d/physics_joint.h";
+#include "scene/resources/capsule_shape.h"
+#include "scene/resources/sphere_shape.h"
+#include "spatial_editor_plugin.h"
+
+void SkeletonEditor::_on_click_option(int p_option) {
+	if (!skeleton) {
+		return;
+	}
+
+	switch (p_option) {
+		case MENU_OPTION_CREATE_PHYSICAL_SKELETON: {
+			create_physical_skeleton();
+		} break;
+	}
+}
+
+void SkeletonEditor::create_physical_skeleton() {
+	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	Node *owner = skeleton == get_tree()->get_edited_scene_root() ? skeleton : skeleton->get_owner();
+
+	const int bc = skeleton->get_bone_count();
+
+	if (!bc) {
+		return;
+	}
+
+	Vector<BoneInfo> bones_infos;
+	bones_infos.resize(bc);
+
+	for (int bone_id = 0; bc > bone_id; ++bone_id) {
+
+		const int parent = skeleton->get_bone_parent(bone_id);
+		const int parent_parent = skeleton->get_bone_parent(parent);
+
+		if (parent < 0) {
+
+			bones_infos[bone_id].relative_rest = skeleton->get_bone_rest(bone_id);
+
+		} else {
+
+			bones_infos[bone_id].relative_rest = bones_infos[parent].relative_rest * skeleton->get_bone_rest(bone_id);
+
+			/// create physical bone on parent
+			if (!bones_infos[parent].physical_bone) {
+
+				bones_infos[parent].physical_bone = create_physical_bone(parent, bone_id, bones_infos);
+
+				ur->create_action(TTR("Create physical bones"));
+				ur->add_do_method(skeleton, "add_child", bones_infos[parent].physical_bone);
+				ur->add_do_reference(bones_infos[parent].physical_bone);
+				ur->add_undo_method(skeleton, "remove_child", bones_infos[parent].physical_bone);
+				ur->commit_action();
+
+				bones_infos[parent].physical_bone->set_bone_name(skeleton->get_bone_name(parent));
+				bones_infos[parent].physical_bone->set_owner(owner);
+				bones_infos[parent].physical_bone->get_child(0)->set_owner(owner); // set shape owner
+
+				/// Create joint between parent of parent
+				if (-1 != parent_parent) {
+
+					bones_infos[parent].physical_bone->set_joint_type(PhysicalBone::JOINT_TYPE_PIN);
+				}
+			}
+		}
+	}
+}
+
+PhysicalBone *SkeletonEditor::create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos) {
+
+	real_t half_height(skeleton->get_bone_rest(bone_child_id).origin.length() * 0.5);
+	real_t radius(half_height * 0.2);
+
+	CapsuleShape *bone_shape_capsule = memnew(CapsuleShape);
+	bone_shape_capsule->set_height((half_height - radius) * 2);
+	bone_shape_capsule->set_radius(radius);
+
+	CollisionShape *bone_shape = memnew(CollisionShape);
+	bone_shape->set_shape(bone_shape_capsule);
+
+	Transform body_transform;
+	body_transform.origin = Vector3(0, 0, -half_height);
+
+	Transform joint_transform;
+	joint_transform.origin = Vector3(0, 0, half_height);
+
+	PhysicalBone *physical_bone = memnew(PhysicalBone);
+	physical_bone->add_child(bone_shape);
+	physical_bone->set_name("Physical Bone " + skeleton->get_bone_name(bone_id));
+	physical_bone->set_body_offset(body_transform);
+	physical_bone->set_joint_offset(joint_transform);
+	return physical_bone;
+}
+
+void SkeletonEditor::edit(Skeleton *p_node) {
+	skeleton = p_node;
+}
+
+void SkeletonEditor::_node_removed(Node *p_node) {
+
+	if (p_node == skeleton) {
+		skeleton = NULL;
+		options->hide();
+	}
+}
+
+void SkeletonEditor::_bind_methods() {
+	ClassDB::bind_method("_on_click_option", &SkeletonEditor::_on_click_option);
+}
+
+SkeletonEditor::SkeletonEditor() {
+	options = memnew(MenuButton);
+	SpatialEditor::get_singleton()->add_control_to_menu_panel(options);
+
+	options->set_text(TTR("Skeleton"));
+	options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Skeleton", "EditorIcons"));
+
+	options->get_popup()->add_item(TTR("Create physical skeleton"), MENU_OPTION_CREATE_PHYSICAL_SKELETON);
+
+	options->get_popup()->connect("id_pressed", this, "_on_click_option");
+	options->hide();
+}
+
+SkeletonEditor::~SkeletonEditor() {
+	SpatialEditor::get_singleton()->remove_child(options);
+	memdelete(options);
+}
+
+void SkeletonEditorPlugin::edit(Object *p_object) {
+	skeleton_editor->edit(Object::cast_to<Skeleton>(p_object));
+}
+
+bool SkeletonEditorPlugin::handles(Object *p_object) const {
+	return p_object->is_class("Skeleton");
+}
+
+void SkeletonEditorPlugin::make_visible(bool p_visible) {
+	if (p_visible) {
+		skeleton_editor->options->show();
+	} else {
+
+		skeleton_editor->options->hide();
+		skeleton_editor->edit(NULL);
+	}
+}
+
+SkeletonEditorPlugin::SkeletonEditorPlugin(EditorNode *p_node) {
+	editor = p_node;
+	skeleton_editor = memnew(SkeletonEditor);
+	editor->get_viewport()->add_child(skeleton_editor);
+}
+
+SkeletonEditorPlugin::~SkeletonEditorPlugin() {
+	editor->get_viewport()->remove_child(skeleton_editor);
+	memdelete(skeleton_editor);
+}

+ 95 - 0
editor/plugins/skeleton_editor_plugin.h

@@ -0,0 +1,95 @@
+/*************************************************************************/
+/*  skeleton_editor_plugin.h                                             */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef SKELETON_EDITOR_PLUGIN_H
+#define SKELETON_EDITOR_PLUGIN_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "scene/3d/skeleton.h"
+
+class PhysicalBone;
+class Joint;
+
+class SkeletonEditor : public Node {
+	GDCLASS(SkeletonEditor, Node);
+
+	enum Menu {
+		MENU_OPTION_CREATE_PHYSICAL_SKELETON
+	};
+
+	struct BoneInfo {
+		PhysicalBone *physical_bone;
+		Transform relative_rest; // Relative to skeleton node
+		BoneInfo() :
+				physical_bone(NULL) {}
+	};
+
+	Skeleton *skeleton;
+
+	MenuButton *options;
+
+	void _on_click_option(int p_option);
+
+	friend class SkeletonEditorPlugin;
+
+protected:
+	void _node_removed(Node *p_node);
+	static void _bind_methods();
+
+	void create_physical_skeleton();
+	PhysicalBone *create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos);
+
+public:
+	void edit(Skeleton *p_mesh);
+
+	SkeletonEditor();
+	~SkeletonEditor();
+};
+
+class SkeletonEditorPlugin : public EditorPlugin {
+
+	GDCLASS(SkeletonEditorPlugin, EditorPlugin);
+
+	EditorNode *editor;
+	SkeletonEditor *skeleton_editor;
+
+public:
+	virtual String get_name() const { return "Skeleton"; }
+	virtual 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);
+
+	SkeletonEditorPlugin(EditorNode *p_node);
+	~SkeletonEditorPlugin();
+};
+
+#endif // SKELETON_EDITOR_PLUGIN_H

+ 20 - 11
editor/plugins/spatial_editor_plugin.cpp

@@ -72,6 +72,14 @@
 #define MIN_FOV 0.01
 #define MAX_FOV 179
 
+#ifdef TOOLS_ENABLED
+#define get_global_gizmo_transform get_global_gizmo_transform
+#define get_local_gizmo_transform get_local_gizmo_transform
+#else
+#define get_global_gizmo_transform get_global_transform
+#define get_local_gizmo_transform get_transform
+#endif
+
 void SpatialEditorViewport::_update_camera(float p_interp_delta) {
 
 	bool is_orthogonal = camera->get_projection() == Camera::PROJECTION_ORTHOGONAL;
@@ -584,8 +592,8 @@ void SpatialEditorViewport::_compute_edit(const Point2 &p_point) {
 		if (!se)
 			continue;
 
-		se->original = se->sp->get_global_transform();
-		se->original_local = se->sp->get_transform();
+		se->original = se->sp->get_global_gizmo_transform();
+		se->original_local = se->sp->get_local_gizmo_transform();
 	}
 }
 
@@ -1184,7 +1192,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
 							if (!se)
 								continue;
 
-							undo_redo->add_do_method(sp, "set_global_transform", sp->get_global_transform());
+							undo_redo->add_do_method(sp, "set_global_transform", sp->get_global_gizmo_transform());
 							undo_redo->add_undo_method(sp, "set_global_transform", se->original);
 						}
 						undo_redo->commit_action();
@@ -2150,7 +2158,7 @@ void SpatialEditorViewport::_notification(int p_what) {
 				se->aabb = vi ? vi->get_aabb() : AABB(Vector3(-0.2, -0.2, -0.2), Vector3(0.4, 0.4, 0.4));
 			}
 
-			Transform t = sp->get_global_transform();
+			Transform t = sp->get_global_gizmo_transform();
 			t.translate(se->aabb.position);
 
 			// apply AABB scaling before item's global transform
@@ -2503,7 +2511,7 @@ void SpatialEditorViewport::_menu_option(int p_option) {
 				xform.scale_basis(sp->get_scale());
 
 				undo_redo->add_do_method(sp, "set_global_transform", xform);
-				undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_transform());
+				undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_gizmo_transform());
 			}
 			undo_redo->commit_action();
 		} break;
@@ -2961,7 +2969,7 @@ void SpatialEditorViewport::focus_selection() {
 		if (!se)
 			continue;
 
-		center += sp->get_global_transform().origin;
+		center += sp->get_global_gizmo_transform().origin;
 		count++;
 	}
 
@@ -3043,7 +3051,7 @@ AABB SpatialEditorViewport::_calculate_spatial_bounds(const Spatial *p_parent, c
 			MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(child);
 			if (mesh_instance) {
 				AABB mesh_instance_bounds = mesh_instance->get_aabb();
-				mesh_instance_bounds.position += mesh_instance->get_global_transform().origin - p_parent->get_global_transform().origin;
+				mesh_instance_bounds.position += mesh_instance->get_global_gizmo_transform().origin - p_parent->get_global_gizmo_transform().origin;
 				bounds.merge_with(mesh_instance_bounds);
 			}
 			bounds = _calculate_spatial_bounds(child, bounds);
@@ -3154,7 +3162,7 @@ bool SpatialEditorViewport::_create_instance(Node *parent, String &path, const P
 	Transform global_transform;
 	Spatial *parent_spatial = Object::cast_to<Spatial>(parent);
 	if (parent_spatial)
-		global_transform = parent_spatial->get_global_transform();
+		global_transform = parent_spatial->get_global_gizmo_transform();
 
 	global_transform.origin = spatial_editor->snap_point(_get_instance_position(p_point));
 
@@ -3787,7 +3795,8 @@ void SpatialEditor::update_transform_gizmo() {
 		if (!se)
 			continue;
 
-		Transform xf = se->sp->get_global_transform();
+		Transform xf = se->sp->get_global_gizmo_transform();
+
 		if (first) {
 			center.position = xf.origin;
 			first = false;
@@ -4054,7 +4063,7 @@ void SpatialEditor::_xform_dialog_action() {
 
 		bool post = xform_type->get_selected() > 0;
 
-		Transform tr = sp->get_global_transform();
+		Transform tr = sp->get_global_gizmo_transform();
 		if (post)
 			tr = tr * t;
 		else {
@@ -4064,7 +4073,7 @@ void SpatialEditor::_xform_dialog_action() {
 		}
 
 		undo_redo->add_do_method(sp, "set_global_transform", tr);
-		undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_transform());
+		undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_gizmo_transform());
 	}
 	undo_redo->commit_action();
 }

+ 120 - 0
editor/spatial_editor_gizmos.cpp

@@ -1534,6 +1534,120 @@ SkeletonSpatialGizmo::SkeletonSpatialGizmo(Skeleton *p_skel) {
 	set_spatial_node(p_skel);
 }
 
+PhysicalBoneSpatialGizmo::PhysicalBoneSpatialGizmo(PhysicalBone *p_pb) :
+		EditorSpatialGizmo(),
+		physical_bone(p_pb) {
+	set_spatial_node(p_pb);
+}
+
+void PhysicalBoneSpatialGizmo::redraw() {
+
+	clear();
+
+	if (!physical_bone)
+		return;
+
+	Skeleton *sk(physical_bone->find_skeleton_parent());
+	PhysicalBone *pb(sk->get_physical_bone(physical_bone->get_bone_id()));
+	PhysicalBone *pbp(sk->get_physical_bone_parent(physical_bone->get_bone_id()));
+
+	Vector<Vector3> points;
+
+	switch (physical_bone->get_joint_type()) {
+		case PhysicalBone::JOINT_TYPE_PIN: {
+
+			PinJointSpatialGizmo::CreateGizmo(physical_bone->get_joint_offset(), points);
+		} break;
+		case PhysicalBone::JOINT_TYPE_CONE: {
+
+			const PhysicalBone::ConeJointData *cjd(static_cast<const PhysicalBone::ConeJointData *>(physical_bone->get_joint_data()));
+			ConeTwistJointSpatialGizmo::CreateGizmo(
+					physical_bone->get_joint_offset(),
+					physical_bone->get_global_transform() * physical_bone->get_joint_offset(),
+					pb ? pb->get_global_transform() : Transform(),
+					pbp ? pbp->get_global_transform() : Transform(),
+					cjd->swing_span,
+					cjd->twist_span,
+					points,
+					pb ? &points : NULL,
+					pbp ? &points : NULL);
+		} break;
+		case PhysicalBone::JOINT_TYPE_HINGE: {
+
+			const PhysicalBone::HingeJointData *hjd(static_cast<const PhysicalBone::HingeJointData *>(physical_bone->get_joint_data()));
+			HingeJointSpatialGizmo::CreateGizmo(
+					physical_bone->get_joint_offset(),
+					physical_bone->get_global_transform() * physical_bone->get_joint_offset(),
+					pb ? pb->get_global_transform() : Transform(),
+					pbp ? pbp->get_global_transform() : Transform(),
+					hjd->angular_limit_lower,
+					hjd->angular_limit_upper,
+					hjd->angular_limit_enabled,
+					points,
+					pb ? &points : NULL,
+					pbp ? &points : NULL);
+		} break;
+		case PhysicalBone::JOINT_TYPE_SLIDER: {
+
+			const PhysicalBone::SliderJointData *sjd(static_cast<const PhysicalBone::SliderJointData *>(physical_bone->get_joint_data()));
+			SliderJointSpatialGizmo::CreateGizmo(
+					physical_bone->get_joint_offset(),
+					physical_bone->get_global_transform() * physical_bone->get_joint_offset(),
+					pb ? pb->get_global_transform() : Transform(),
+					pbp ? pbp->get_global_transform() : Transform(),
+					sjd->angular_limit_lower,
+					sjd->angular_limit_upper,
+					sjd->linear_limit_lower,
+					sjd->linear_limit_upper,
+					points,
+					pb ? &points : NULL,
+					pbp ? &points : NULL);
+		} break;
+		case PhysicalBone::JOINT_TYPE_6DOF: {
+
+			const PhysicalBone::SixDOFJointData *sdofjd(static_cast<const PhysicalBone::SixDOFJointData *>(physical_bone->get_joint_data()));
+			Generic6DOFJointSpatialGizmo::CreateGizmo(
+					physical_bone->get_joint_offset(),
+
+					physical_bone->get_global_transform() * physical_bone->get_joint_offset(),
+					pb ? pb->get_global_transform() : Transform(),
+					pbp ? pbp->get_global_transform() : Transform(),
+
+					sdofjd->axis_data[0].angular_limit_lower,
+					sdofjd->axis_data[0].angular_limit_upper,
+					sdofjd->axis_data[0].linear_limit_lower,
+					sdofjd->axis_data[0].linear_limit_upper,
+					sdofjd->axis_data[0].angular_limit_enabled,
+					sdofjd->axis_data[0].linear_limit_enabled,
+
+					sdofjd->axis_data[1].angular_limit_lower,
+					sdofjd->axis_data[1].angular_limit_upper,
+					sdofjd->axis_data[1].linear_limit_lower,
+					sdofjd->axis_data[1].linear_limit_upper,
+					sdofjd->axis_data[1].angular_limit_enabled,
+					sdofjd->axis_data[1].linear_limit_enabled,
+
+					sdofjd->axis_data[2].angular_limit_lower,
+					sdofjd->axis_data[2].angular_limit_upper,
+					sdofjd->axis_data[2].linear_limit_lower,
+					sdofjd->axis_data[2].linear_limit_upper,
+					sdofjd->axis_data[2].angular_limit_enabled,
+					sdofjd->axis_data[2].linear_limit_enabled,
+
+					points,
+					pb ? &points : NULL,
+					pbp ? &points : NULL);
+		} break;
+		default:
+			return;
+	}
+
+	Ref<Material> material = create_material("joint_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint"));
+
+	add_collision_segments(points);
+	add_lines(points, material);
+}
+
 // FIXME: Kept as reference for reimplementation in 3.1+
 #if 0
 void RoomSpatialGizmo::redraw() {
@@ -3735,6 +3849,12 @@ Ref<SpatialEditorGizmo> SpatialEditorGizmos::get_gizmo(Spatial *p_spatial) {
 		return lsg;
 	}
 
+	if (Object::cast_to<PhysicalBone>(p_spatial)) {
+
+		Ref<PhysicalBoneSpatialGizmo> pbsg = memnew(PhysicalBoneSpatialGizmo(Object::cast_to<PhysicalBone>(p_spatial)));
+		return pbsg;
+	}
+
 	if (Object::cast_to<Position3D>(p_spatial)) {
 
 		Ref<Position3DSpatialGizmo> lsg = memnew(Position3DSpatialGizmo(Object::cast_to<Position3D>(p_spatial)));

+ 11 - 0
editor/spatial_editor_gizmos.h

@@ -214,6 +214,17 @@ public:
 	SkeletonSpatialGizmo(Skeleton *p_skel = NULL);
 };
 
+class PhysicalBoneSpatialGizmo : public EditorSpatialGizmo {
+	GDCLASS(PhysicalBoneSpatialGizmo, EditorSpatialGizmo);
+
+	PhysicalBone *physical_bone;
+
+public:
+	//virtual Transform get_global_gizmo_transform();
+	virtual void redraw();
+	PhysicalBoneSpatialGizmo(PhysicalBone *p_pb = NULL);
+};
+
 #if 0
 class PortalSpatialGizmo : public EditorSpatialGizmo {
 

+ 1 - 1
modules/bullet/godot_result_callbacks.cpp

@@ -110,7 +110,7 @@ bool GodotKinClosestConvexResultCallback::needsCollision(btBroadphaseProxy *prox
 			if (gObj->getType() == CollisionObjectBullet::TYPE_AREA)
 				return false;
 
-			if (m_self_object->has_collision_exception(gObj))
+			if (m_self_object->has_collision_exception(gObj) || gObj->has_collision_exception(m_self_object))
 				return false;
 		}
 		return true;

+ 1109 - 0
scene/3d/physics_body.cpp

@@ -34,6 +34,10 @@
 #include "method_bind_ext.gen.inc"
 #include "scene/scene_string_names.h"
 
+#ifdef TOOLS_ENABLED
+#include "editor/plugins/spatial_editor_plugin.h"
+#endif
+
 void PhysicsBody::_notification(int p_what) {
 
 	/*
@@ -1266,3 +1270,1108 @@ KinematicCollision::KinematicCollision() {
 	collision.local_shape = 0;
 	owner = NULL;
 }
+
+///////////////////////////////////////
+
+bool PhysicalBone::JointData::_set(const StringName &p_name, const Variant &p_value, RID j) {
+	return false;
+}
+
+bool PhysicalBone::JointData::_get(const StringName &p_name, Variant &r_ret) const {
+	return false;
+}
+
+void PhysicalBone::JointData::_get_property_list(List<PropertyInfo> *p_list) const {
+}
+
+bool PhysicalBone::PinJointData::_set(const StringName &p_name, const Variant &p_value, RID j) {
+	if (JointData::_set(p_name, p_value, j)) {
+		return true;
+	}
+
+	if ("joint_constraints/bias" == p_name) {
+		bias = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->pin_joint_set_param(j, PhysicsServer::PIN_JOINT_BIAS, bias);
+
+	} else if ("joint_constraints/damping" == p_name) {
+		damping = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->pin_joint_set_param(j, PhysicsServer::PIN_JOINT_DAMPING, damping);
+
+	} else if ("joint_constraints/impulse_clamp" == p_name) {
+		impulse_clamp = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->pin_joint_set_param(j, PhysicsServer::PIN_JOINT_IMPULSE_CLAMP, impulse_clamp);
+
+	} else {
+		return false;
+	}
+
+	return true;
+}
+
+bool PhysicalBone::PinJointData::_get(const StringName &p_name, Variant &r_ret) const {
+	if (JointData::_get(p_name, r_ret)) {
+		return true;
+	}
+
+	if ("joint_constraints/bias" == p_name) {
+		r_ret = bias;
+	} else if ("joint_constraints/damping" == p_name) {
+		r_ret = damping;
+	} else if ("joint_constraints/impulse_clamp" == p_name) {
+		r_ret = impulse_clamp;
+	} else {
+		return false;
+	}
+
+	return true;
+}
+
+void PhysicalBone::PinJointData::_get_property_list(List<PropertyInfo> *p_list) const {
+	JointData::_get_property_list(p_list);
+
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/bias", PROPERTY_HINT_RANGE, "0.01,0.99,0.01"));
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/damping", PROPERTY_HINT_RANGE, "0.01,8.0,0.01"));
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/impulse_clamp", PROPERTY_HINT_RANGE, "0.0,64.0,0.01"));
+}
+
+bool PhysicalBone::ConeJointData::_set(const StringName &p_name, const Variant &p_value, RID j) {
+	if (JointData::_set(p_name, p_value, j)) {
+		return true;
+	}
+
+	if ("joint_constraints/swing_span" == p_name) {
+		swing_span = Math::deg2rad(real_t(p_value));
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer::CONE_TWIST_JOINT_SWING_SPAN, swing_span);
+
+	} else if ("joint_constraints/twist_span" == p_name) {
+		twist_span = Math::deg2rad(real_t(p_value));
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer::CONE_TWIST_JOINT_TWIST_SPAN, twist_span);
+
+	} else if ("joint_constraints/bias" == p_name) {
+		bias = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer::CONE_TWIST_JOINT_BIAS, bias);
+
+	} else if ("joint_constraints/softness" == p_name) {
+		softness = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer::CONE_TWIST_JOINT_SOFTNESS, softness);
+
+	} else if ("joint_constraints/relaxation" == p_name) {
+		relaxation = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer::CONE_TWIST_JOINT_RELAXATION, relaxation);
+
+	} else {
+		return false;
+	}
+
+	return true;
+}
+
+bool PhysicalBone::ConeJointData::_get(const StringName &p_name, Variant &r_ret) const {
+	if (JointData::_get(p_name, r_ret)) {
+		return true;
+	}
+
+	if ("joint_constraints/swing_span" == p_name) {
+		r_ret = Math::rad2deg(swing_span);
+	} else if ("joint_constraints/twist_span" == p_name) {
+		r_ret = Math::rad2deg(twist_span);
+	} else if ("joint_constraints/bias" == p_name) {
+		r_ret = bias;
+	} else if ("joint_constraints/softness" == p_name) {
+		r_ret = softness;
+	} else if ("joint_constraints/relaxation" == p_name) {
+		r_ret = relaxation;
+	} else {
+		return false;
+	}
+
+	return true;
+}
+
+void PhysicalBone::ConeJointData::_get_property_list(List<PropertyInfo> *p_list) const {
+	JointData::_get_property_list(p_list);
+
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/swing_span", PROPERTY_HINT_RANGE, "-180,180,0.01"));
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/twist_span", PROPERTY_HINT_RANGE, "-40000,40000,0.1"));
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/bias", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/relaxation", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
+}
+
+bool PhysicalBone::HingeJointData::_set(const StringName &p_name, const Variant &p_value, RID j) {
+	if (JointData::_set(p_name, p_value, j)) {
+		return true;
+	}
+
+	if ("joint_constraints/angular_limit_enabled" == p_name) {
+		angular_limit_enabled = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->hinge_joint_set_flag(j, PhysicsServer::HINGE_JOINT_FLAG_USE_LIMIT, angular_limit_enabled);
+
+	} else if ("joint_constraints/angular_limit_upper" == p_name) {
+		angular_limit_upper = Math::deg2rad(real_t(p_value));
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->hinge_joint_set_param(j, PhysicsServer::HINGE_JOINT_LIMIT_UPPER, angular_limit_upper);
+
+	} else if ("joint_constraints/angular_limit_lower" == p_name) {
+		angular_limit_lower = Math::deg2rad(real_t(p_value));
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->hinge_joint_set_param(j, PhysicsServer::HINGE_JOINT_LIMIT_LOWER, angular_limit_lower);
+
+	} else if ("joint_constraints/angular_limit_bias" == p_name) {
+		angular_limit_bias = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->hinge_joint_set_param(j, PhysicsServer::HINGE_JOINT_LIMIT_BIAS, angular_limit_bias);
+
+	} else if ("joint_constraints/angular_limit_softness" == p_name) {
+		angular_limit_softness = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->hinge_joint_set_param(j, PhysicsServer::HINGE_JOINT_LIMIT_SOFTNESS, angular_limit_softness);
+
+	} else if ("joint_constraints/angular_limit_relaxation" == p_name) {
+		angular_limit_relaxation = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->hinge_joint_set_param(j, PhysicsServer::HINGE_JOINT_LIMIT_RELAXATION, angular_limit_relaxation);
+
+	} else {
+		return false;
+	}
+
+	return true;
+}
+
+bool PhysicalBone::HingeJointData::_get(const StringName &p_name, Variant &r_ret) const {
+	if (JointData::_get(p_name, r_ret)) {
+		return true;
+	}
+
+	if ("joint_constraints/angular_limit_enabled" == p_name) {
+		r_ret = angular_limit_enabled;
+	} else if ("joint_constraints/angular_limit_upper" == p_name) {
+		r_ret = Math::rad2deg(angular_limit_upper);
+	} else if ("joint_constraints/angular_limit_lower" == p_name) {
+		r_ret = Math::rad2deg(angular_limit_lower);
+	} else if ("joint_constraints/angular_limit_bias" == p_name) {
+		r_ret = angular_limit_bias;
+	} else if ("joint_constraints/angular_limit_softness" == p_name) {
+		r_ret = angular_limit_softness;
+	} else if ("joint_constraints/angular_limit_relaxation" == p_name) {
+		r_ret = angular_limit_relaxation;
+	} else {
+		return false;
+	}
+
+	return true;
+}
+
+void PhysicalBone::HingeJointData::_get_property_list(List<PropertyInfo> *p_list) const {
+	JointData::_get_property_list(p_list);
+
+	p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/angular_limit_enabled"));
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_upper", PROPERTY_HINT_RANGE, "-180,180,0.01"));
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_lower", PROPERTY_HINT_RANGE, "-180,180,0.01"));
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_bias", PROPERTY_HINT_RANGE, "0.01,0.99,0.01"));
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_relaxation", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+}
+
+bool PhysicalBone::SliderJointData::_set(const StringName &p_name, const Variant &p_value, RID j) {
+	if (JointData::_set(p_name, p_value, j)) {
+		return true;
+	}
+
+	if ("joint_constraints/linear_limit_upper" == p_name) {
+		linear_limit_upper = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_UPPER, linear_limit_upper);
+
+	} else if ("joint_constraints/linear_limit_lower" == p_name) {
+		linear_limit_lower = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_LOWER, linear_limit_lower);
+
+	} else if ("joint_constraints/linear_limit_softness" == p_name) {
+		linear_limit_softness = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_SOFTNESS, linear_limit_softness);
+
+	} else if ("joint_constraints/linear_limit_restitution" == p_name) {
+		linear_limit_restitution = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_RESTITUTION, linear_limit_restitution);
+
+	} else if ("joint_constraints/linear_limit_damping" == p_name) {
+		linear_limit_damping = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_DAMPING, linear_limit_restitution);
+
+	} else if ("joint_constraints/angular_limit_upper" == p_name) {
+		angular_limit_upper = Math::deg2rad(real_t(p_value));
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_UPPER, angular_limit_upper);
+
+	} else if ("joint_constraints/angular_limit_lower" == p_name) {
+		angular_limit_lower = Math::deg2rad(real_t(p_value));
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_LOWER, angular_limit_lower);
+
+	} else if ("joint_constraints/angular_limit_softness" == p_name) {
+		angular_limit_softness = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS, angular_limit_softness);
+
+	} else if ("joint_constraints/angular_limit_restitution" == p_name) {
+		angular_limit_restitution = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS, angular_limit_softness);
+
+	} else if ("joint_constraints/angular_limit_damping" == p_name) {
+		angular_limit_damping = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_DAMPING, angular_limit_damping);
+
+	} else {
+		return false;
+	}
+
+	return true;
+}
+
+bool PhysicalBone::SliderJointData::_get(const StringName &p_name, Variant &r_ret) const {
+	if (JointData::_get(p_name, r_ret)) {
+		return true;
+	}
+
+	if ("joint_constraints/linear_limit_upper" == p_name) {
+		r_ret = linear_limit_upper;
+	} else if ("joint_constraints/linear_limit_lower" == p_name) {
+		r_ret = linear_limit_lower;
+	} else if ("joint_constraints/linear_limit_softness" == p_name) {
+		r_ret = linear_limit_softness;
+	} else if ("joint_constraints/linear_limit_restitution" == p_name) {
+		r_ret = linear_limit_restitution;
+	} else if ("joint_constraints/linear_limit_damping" == p_name) {
+		r_ret = linear_limit_damping;
+	} else if ("joint_constraints/angular_limit_upper" == p_name) {
+		r_ret = Math::rad2deg(angular_limit_upper);
+	} else if ("joint_constraints/angular_limit_lower" == p_name) {
+		r_ret = Math::rad2deg(angular_limit_lower);
+	} else if ("joint_constraints/angular_limit_softness" == p_name) {
+		r_ret = angular_limit_softness;
+	} else if ("joint_constraints/angular_limit_restitution" == p_name) {
+		r_ret = angular_limit_restitution;
+	} else if ("joint_constraints/angular_limit_damping" == p_name) {
+		r_ret = angular_limit_damping;
+	} else {
+		return false;
+	}
+
+	return true;
+}
+
+void PhysicalBone::SliderJointData::_get_property_list(List<PropertyInfo> *p_list) const {
+	JointData::_get_property_list(p_list);
+
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/linear_limit_upper"));
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/linear_limit_lower"));
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/linear_limit_softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/linear_limit_restitution", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/linear_limit_damping", PROPERTY_HINT_RANGE, "0,16.0,0.01"));
+
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_upper", PROPERTY_HINT_RANGE, "-180,180,0.01"));
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_lower", PROPERTY_HINT_RANGE, "-180,180,0.01"));
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_restitution", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
+	p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_damping", PROPERTY_HINT_RANGE, "0,16.0,0.01"));
+}
+
+bool PhysicalBone::SixDOFJointData::_set(const StringName &p_name, const Variant &p_value, RID j) {
+	if (JointData::_set(p_name, p_value, j)) {
+		return true;
+	}
+
+	String path = p_name;
+
+	Vector3::Axis axis;
+	{
+		const String axis_s = path.get_slicec('/', 1);
+		if ("x" == axis_s) {
+			axis = Vector3::AXIS_X;
+		} else if ("y" == axis_s) {
+			axis = Vector3::AXIS_Y;
+		} else if ("z" == axis_s) {
+			axis = Vector3::AXIS_Z;
+		} else {
+			return false;
+		}
+	}
+
+	String var_name = path.get_slicec('/', 2);
+
+	if ("linear_limit_enabled" == var_name) {
+		axis_data[axis].linear_limit_enabled = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(j, axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT, axis_data[axis].linear_limit_enabled);
+
+	} else if ("linear_limit_upper" == var_name) {
+		axis_data[axis].linear_limit_upper = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_UPPER_LIMIT, axis_data[axis].linear_limit_upper);
+
+	} else if ("linear_limit_lower" == var_name) {
+		axis_data[axis].linear_limit_lower = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_LOWER_LIMIT, axis_data[axis].linear_limit_lower);
+
+	} else if ("linear_limit_softness" == var_name) {
+		axis_data[axis].linear_limit_softness = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS, axis_data[axis].linear_limit_softness);
+
+	} else if ("linear_restitution" == var_name) {
+		axis_data[axis].linear_restitution = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_RESTITUTION, axis_data[axis].linear_restitution);
+
+	} else if ("linear_damping" == var_name) {
+		axis_data[axis].linear_damping = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING, axis_data[axis].linear_damping);
+
+	} else if ("angular_limit_enabled" == var_name) {
+		axis_data[axis].angular_limit_enabled = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(j, axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, axis_data[axis].angular_limit_enabled);
+
+	} else if ("angular_limit_upper" == var_name) {
+		axis_data[axis].angular_limit_upper = Math::deg2rad(real_t(p_value));
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT, axis_data[axis].angular_limit_upper);
+
+	} else if ("angular_limit_lower" == var_name) {
+		axis_data[axis].angular_limit_lower = Math::deg2rad(real_t(p_value));
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT, axis_data[axis].angular_limit_lower);
+
+	} else if ("angular_limit_softness" == var_name) {
+		axis_data[axis].angular_limit_softness = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS, axis_data[axis].angular_limit_softness);
+
+	} else if ("angular_restitution" == var_name) {
+		axis_data[axis].angular_restitution = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_RESTITUTION, axis_data[axis].angular_restitution);
+
+	} else if ("angular_damping" == var_name) {
+		axis_data[axis].angular_damping = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_DAMPING, axis_data[axis].angular_damping);
+
+	} else if ("erp" == var_name) {
+		axis_data[axis].erp = p_value;
+		if (j.is_valid())
+			PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_ERP, axis_data[axis].erp);
+
+	} else {
+		return false;
+	}
+
+	return true;
+}
+
+bool PhysicalBone::SixDOFJointData::_get(const StringName &p_name, Variant &r_ret) const {
+	if (JointData::_get(p_name, r_ret)) {
+		return true;
+	}
+
+	String path = p_name;
+
+	int axis;
+	{
+		const String axis_s = path.get_slicec('/', 1);
+		if ("x" == axis_s) {
+			axis = 0;
+		} else if ("y" == axis_s) {
+			axis = 1;
+		} else if ("z" == axis_s) {
+			axis = 2;
+		} else {
+			return false;
+		}
+	}
+
+	String var_name = path.get_slicec('/', 2);
+
+	if ("linear_limit_enabled" == var_name) {
+		r_ret = axis_data[axis].linear_limit_enabled;
+	} else if ("linear_limit_upper" == var_name) {
+		r_ret = axis_data[axis].linear_limit_upper;
+	} else if ("linear_limit_lower" == var_name) {
+		r_ret = axis_data[axis].linear_limit_lower;
+	} else if ("linear_limit_softness" == var_name) {
+		r_ret = axis_data[axis].linear_limit_softness;
+	} else if ("linear_restitution" == var_name) {
+		r_ret = axis_data[axis].linear_restitution;
+	} else if ("linear_damping" == var_name) {
+		r_ret = axis_data[axis].linear_damping;
+	} else if ("angular_limit_enabled" == var_name) {
+		r_ret = axis_data[axis].angular_limit_enabled;
+	} else if ("angular_limit_upper" == var_name) {
+		r_ret = Math::rad2deg(axis_data[axis].angular_limit_upper);
+	} else if ("angular_limit_lower" == var_name) {
+		r_ret = Math::rad2deg(axis_data[axis].angular_limit_lower);
+	} else if ("angular_limit_softness" == var_name) {
+		r_ret = axis_data[axis].angular_limit_softness;
+	} else if ("angular_restitution" == var_name) {
+		r_ret = axis_data[axis].angular_restitution;
+	} else if ("angular_damping" == var_name) {
+		r_ret = axis_data[axis].angular_damping;
+	} else if ("erp" == var_name) {
+		r_ret = axis_data[axis].erp;
+	} else {
+		return false;
+	}
+
+	return true;
+}
+
+void PhysicalBone::SixDOFJointData::_get_property_list(List<PropertyInfo> *p_list) const {
+	const StringName axis_names[] = { "x", "y", "z" };
+	for (int i = 0; i < 3; ++i) {
+		p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/" + axis_names[i] + "/linear_limit_enabled"));
+		p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_limit_upper"));
+		p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_limit_lower"));
+		p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_limit_softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+		p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+		p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+		p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/" + axis_names[i] + "/angular_limit_enabled"));
+		p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_limit_upper", PROPERTY_HINT_RANGE, "-180,180,0.01"));
+		p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_limit_lower", PROPERTY_HINT_RANGE, "-180,180,0.01"));
+		p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_limit_softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+		p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+		p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+		p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/erp"));
+	}
+}
+
+bool PhysicalBone::_set(const StringName &p_name, const Variant &p_value) {
+	if (p_name == "bone_name") {
+		set_bone_name(p_value);
+		return true;
+	}
+
+	if (joint_data) {
+		if (joint_data->_set(p_name, p_value)) {
+#ifdef TOOLS_ENABLED
+			if (get_gizmo().is_valid())
+				get_gizmo()->redraw();
+#endif
+			return true;
+		}
+	}
+
+	return false;
+}
+
+bool PhysicalBone::_get(const StringName &p_name, Variant &r_ret) const {
+	if (p_name == "bone_name") {
+		r_ret = get_bone_name();
+		return true;
+	}
+
+	if (joint_data) {
+		return joint_data->_get(p_name, r_ret);
+	}
+
+	return false;
+}
+
+void PhysicalBone::_get_property_list(List<PropertyInfo> *p_list) const {
+
+	Skeleton *parent = find_skeleton_parent(get_parent());
+
+	if (parent) {
+
+		String names;
+		for (int i = 0; i < parent->get_bone_count(); i++) {
+			if (i > 0)
+				names += ",";
+			names += parent->get_bone_name(i);
+		}
+
+		p_list->push_back(PropertyInfo(Variant::STRING, "bone_name", PROPERTY_HINT_ENUM, names));
+	} else {
+
+		p_list->push_back(PropertyInfo(Variant::STRING, "bone_name"));
+	}
+
+	if (joint_data) {
+		joint_data->_get_property_list(p_list);
+	}
+}
+
+void PhysicalBone::_notification(int p_what) {
+	switch (p_what) {
+		case NOTIFICATION_ENTER_TREE:
+			parent_skeleton = find_skeleton_parent(get_parent());
+			update_bone_id();
+			reset_to_rest_position();
+			break;
+		case NOTIFICATION_EXIT_TREE:
+			if (parent_skeleton) {
+				if (-1 != bone_id) {
+					parent_skeleton->unbind_physical_bone_from_bone(bone_id);
+				}
+			}
+			parent_skeleton = NULL;
+			update_bone_id();
+			break;
+		case NOTIFICATION_TRANSFORM_CHANGED:
+			if (Engine::get_singleton()->is_editor_hint()) {
+
+				update_offset();
+			}
+			break;
+	}
+}
+
+void PhysicalBone::_direct_state_changed(Object *p_state) {
+
+	if (!simulate_physics) {
+		return;
+	}
+
+	/// Update bone transform
+
+	PhysicsDirectBodyState *state;
+
+#ifdef DEBUG_ENABLED
+	state = Object::cast_to<PhysicsDirectBodyState>(p_state);
+#else
+	state = (PhysicsDirectBodyState *)p_state; //trust it
+#endif
+
+	Transform global_transform(state->get_transform());
+
+	set_ignore_transform_notification(true);
+	set_global_transform(global_transform);
+	set_ignore_transform_notification(false);
+
+	// Update skeleton
+	if (parent_skeleton) {
+		if (-1 != bone_id) {
+			parent_skeleton->set_bone_global_pose(bone_id, parent_skeleton->get_global_transform().affine_inverse() * (global_transform * body_offset_inverse));
+		}
+	}
+}
+
+void PhysicalBone::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("_direct_state_changed"), &PhysicalBone::_direct_state_changed);
+
+	ClassDB::bind_method(D_METHOD("set_joint_type", "joint_type"), &PhysicalBone::set_joint_type);
+	ClassDB::bind_method(D_METHOD("get_joint_type"), &PhysicalBone::get_joint_type);
+
+	ClassDB::bind_method(D_METHOD("set_joint_offset", "offset"), &PhysicalBone::set_joint_offset);
+	ClassDB::bind_method(D_METHOD("get_joint_offset"), &PhysicalBone::get_joint_offset);
+
+	ClassDB::bind_method(D_METHOD("set_body_offset", "offset"), &PhysicalBone::set_body_offset);
+	ClassDB::bind_method(D_METHOD("get_body_offset"), &PhysicalBone::get_body_offset);
+
+	ClassDB::bind_method(D_METHOD("set_static_body", "simulate"), &PhysicalBone::set_static_body);
+	ClassDB::bind_method(D_METHOD("is_static_body"), &PhysicalBone::is_static_body);
+
+	ClassDB::bind_method(D_METHOD("set_simulate_physics", "simulate"), &PhysicalBone::set_simulate_physics);
+	ClassDB::bind_method(D_METHOD("get_simulate_physics"), &PhysicalBone::get_simulate_physics);
+
+	ClassDB::bind_method(D_METHOD("is_simulating_physics"), &PhysicalBone::is_simulating_physics);
+
+	ClassDB::bind_method(D_METHOD("set_mass", "mass"), &PhysicalBone::set_mass);
+	ClassDB::bind_method(D_METHOD("get_mass"), &PhysicalBone::get_mass);
+
+	ClassDB::bind_method(D_METHOD("set_weight", "weight"), &PhysicalBone::set_weight);
+	ClassDB::bind_method(D_METHOD("get_weight"), &PhysicalBone::get_weight);
+
+	ClassDB::bind_method(D_METHOD("set_friction", "friction"), &PhysicalBone::set_friction);
+	ClassDB::bind_method(D_METHOD("get_friction"), &PhysicalBone::get_friction);
+
+	ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &PhysicalBone::set_bounce);
+	ClassDB::bind_method(D_METHOD("get_bounce"), &PhysicalBone::get_bounce);
+
+	ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &PhysicalBone::set_gravity_scale);
+	ClassDB::bind_method(D_METHOD("get_gravity_scale"), &PhysicalBone::get_gravity_scale);
+
+	ADD_GROUP("Joint", "joint_");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "joint_type", PROPERTY_HINT_ENUM, "None,PinJoint,ConeJoint,HingeJoint,SliderJoint,6DOFJoint"), "set_joint_type", "get_joint_type");
+	ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "joint_offset"), "set_joint_offset", "get_joint_offset");
+
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "simulate_physics"), "set_simulate_physics", "get_simulate_physics");
+	ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "body_offset"), "set_body_offset", "get_body_offset");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "static_body"), "set_static_body", "is_static_body");
+
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "mass", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_mass", "get_mass");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_weight", "get_weight");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce");
+	ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_scale", PROPERTY_HINT_RANGE, "-10,10,0.01"), "set_gravity_scale", "get_gravity_scale");
+}
+
+Skeleton *PhysicalBone::find_skeleton_parent(Node *p_parent) {
+	if (!p_parent) {
+		return NULL;
+	}
+	Skeleton *s = Object::cast_to<Skeleton>(p_parent);
+	return s ? s : find_skeleton_parent(p_parent->get_parent());
+}
+
+void PhysicalBone::_fix_joint_offset() {
+	// Clamp joint origin to bone origin
+	if (parent_skeleton) {
+		joint_offset.origin = body_offset.affine_inverse().origin;
+	}
+}
+
+void PhysicalBone::_reload_joint() {
+
+	if (joint.is_valid()) {
+		PhysicsServer::get_singleton()->free(joint);
+		joint = RID();
+	}
+
+	if (!parent_skeleton) {
+		return;
+	}
+
+	PhysicalBone *body_a = parent_skeleton->get_physical_bone_parent(bone_id);
+	if (!body_a) {
+		return;
+	}
+
+	Transform joint_transf = get_global_transform() * joint_offset;
+	Transform local_a = body_a->get_global_transform().affine_inverse() * joint_transf;
+	local_a.orthonormalize();
+
+	switch (get_joint_type()) {
+		case JOINT_TYPE_PIN: {
+
+			joint = PhysicsServer::get_singleton()->joint_create_pin(body_a->get_rid(), local_a.origin, get_rid(), joint_offset.origin);
+			const PinJointData *pjd(static_cast<const PinJointData *>(joint_data));
+			PhysicsServer::get_singleton()->pin_joint_set_param(joint, PhysicsServer::PIN_JOINT_BIAS, pjd->bias);
+			PhysicsServer::get_singleton()->pin_joint_set_param(joint, PhysicsServer::PIN_JOINT_DAMPING, pjd->damping);
+			PhysicsServer::get_singleton()->pin_joint_set_param(joint, PhysicsServer::PIN_JOINT_IMPULSE_CLAMP, pjd->impulse_clamp);
+
+		} break;
+		case JOINT_TYPE_CONE: {
+
+			joint = PhysicsServer::get_singleton()->joint_create_cone_twist(body_a->get_rid(), local_a, get_rid(), joint_offset);
+			const ConeJointData *cjd(static_cast<const ConeJointData *>(joint_data));
+			PhysicsServer::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer::CONE_TWIST_JOINT_SWING_SPAN, cjd->swing_span);
+			PhysicsServer::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer::CONE_TWIST_JOINT_TWIST_SPAN, cjd->twist_span);
+			PhysicsServer::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer::CONE_TWIST_JOINT_BIAS, cjd->bias);
+			PhysicsServer::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer::CONE_TWIST_JOINT_SOFTNESS, cjd->softness);
+			PhysicsServer::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer::CONE_TWIST_JOINT_RELAXATION, cjd->relaxation);
+
+		} break;
+		case JOINT_TYPE_HINGE: {
+
+			joint = PhysicsServer::get_singleton()->joint_create_hinge(body_a->get_rid(), local_a, get_rid(), joint_offset);
+			const HingeJointData *hjd(static_cast<const HingeJointData *>(joint_data));
+			PhysicsServer::get_singleton()->hinge_joint_set_flag(joint, PhysicsServer::HINGE_JOINT_FLAG_USE_LIMIT, hjd->angular_limit_enabled);
+			PhysicsServer::get_singleton()->hinge_joint_set_param(joint, PhysicsServer::HINGE_JOINT_LIMIT_UPPER, hjd->angular_limit_upper);
+			PhysicsServer::get_singleton()->hinge_joint_set_param(joint, PhysicsServer::HINGE_JOINT_LIMIT_LOWER, hjd->angular_limit_lower);
+			PhysicsServer::get_singleton()->hinge_joint_set_param(joint, PhysicsServer::HINGE_JOINT_LIMIT_BIAS, hjd->angular_limit_bias);
+			PhysicsServer::get_singleton()->hinge_joint_set_param(joint, PhysicsServer::HINGE_JOINT_LIMIT_SOFTNESS, hjd->angular_limit_softness);
+			PhysicsServer::get_singleton()->hinge_joint_set_param(joint, PhysicsServer::HINGE_JOINT_LIMIT_RELAXATION, hjd->angular_limit_relaxation);
+
+		} break;
+		case JOINT_TYPE_SLIDER: {
+
+			joint = PhysicsServer::get_singleton()->joint_create_slider(body_a->get_rid(), local_a, get_rid(), joint_offset);
+			const SliderJointData *sjd(static_cast<const SliderJointData *>(joint_data));
+			PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_UPPER, sjd->linear_limit_upper);
+			PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_LOWER, sjd->linear_limit_lower);
+			PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_SOFTNESS, sjd->linear_limit_softness);
+			PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_RESTITUTION, sjd->linear_limit_restitution);
+			PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_DAMPING, sjd->linear_limit_restitution);
+			PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_UPPER, sjd->angular_limit_upper);
+			PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_LOWER, sjd->angular_limit_lower);
+			PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS, sjd->angular_limit_softness);
+			PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS, sjd->angular_limit_softness);
+			PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_DAMPING, sjd->angular_limit_damping);
+
+		} break;
+		case JOINT_TYPE_6DOF: {
+
+			joint = PhysicsServer::get_singleton()->joint_create_generic_6dof(body_a->get_rid(), local_a, get_rid(), joint_offset);
+			const SixDOFJointData *g6dofjd(static_cast<const SixDOFJointData *>(joint_data));
+			for (int axis = 0; axis < 3; ++axis) {
+				PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT, g6dofjd->axis_data[axis].linear_limit_enabled);
+				PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_UPPER_LIMIT, g6dofjd->axis_data[axis].linear_limit_upper);
+				PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_LOWER_LIMIT, g6dofjd->axis_data[axis].linear_limit_lower);
+				PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS, g6dofjd->axis_data[axis].linear_limit_softness);
+				PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_RESTITUTION, g6dofjd->axis_data[axis].linear_restitution);
+				PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING, g6dofjd->axis_data[axis].linear_damping);
+				PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, g6dofjd->axis_data[axis].angular_limit_enabled);
+				PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT, g6dofjd->axis_data[axis].angular_limit_upper);
+				PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT, g6dofjd->axis_data[axis].angular_limit_lower);
+				PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS, g6dofjd->axis_data[axis].angular_limit_softness);
+				PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_RESTITUTION, g6dofjd->axis_data[axis].angular_restitution);
+				PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_DAMPING, g6dofjd->axis_data[axis].angular_damping);
+				PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_ERP, g6dofjd->axis_data[axis].erp);
+			}
+
+		} break;
+	}
+}
+
+void PhysicalBone::_on_bone_parent_changed() {
+	_reload_joint();
+}
+
+void PhysicalBone::_set_gizmo_move_joint(bool p_move_joint) {
+#ifdef TOOLS_ENABLED
+	gizmo_move_joint = p_move_joint;
+	SpatialEditor::get_singleton()->update_transform_gizmo();
+#endif
+}
+
+#ifdef TOOLS_ENABLED
+Transform PhysicalBone::get_global_gizmo_transform() const {
+	return gizmo_move_joint ? get_global_transform() * joint_offset : get_global_transform();
+}
+
+Transform PhysicalBone::get_local_gizmo_transform() const {
+	return gizmo_move_joint ? get_transform() * joint_offset : get_transform();
+}
+#endif
+
+const PhysicalBone::JointData *PhysicalBone::get_joint_data() const {
+	return joint_data;
+}
+
+Skeleton *PhysicalBone::find_skeleton_parent() {
+	return find_skeleton_parent(this);
+}
+
+void PhysicalBone::set_joint_type(JointType p_joint_type) {
+
+	if (p_joint_type == get_joint_type())
+		return;
+
+	memdelete(joint_data);
+	joint_data = NULL;
+	switch (p_joint_type) {
+		case JOINT_TYPE_PIN:
+			joint_data = memnew(PinJointData);
+			break;
+		case JOINT_TYPE_CONE:
+			joint_data = memnew(ConeJointData);
+			break;
+		case JOINT_TYPE_HINGE:
+			joint_data = memnew(HingeJointData);
+			break;
+		case JOINT_TYPE_SLIDER:
+			joint_data = memnew(SliderJointData);
+			break;
+		case JOINT_TYPE_6DOF:
+			joint_data = memnew(SixDOFJointData);
+			break;
+	}
+
+	_reload_joint();
+
+#ifdef TOOLS_ENABLED
+	_change_notify();
+	if (get_gizmo().is_valid())
+		get_gizmo()->redraw();
+#endif
+}
+
+PhysicalBone::JointType PhysicalBone::get_joint_type() const {
+	return joint_data ? joint_data->get_joint_type() : JOINT_TYPE_NONE;
+}
+
+void PhysicalBone::set_joint_offset(const Transform &p_offset) {
+	joint_offset = p_offset;
+
+	_fix_joint_offset();
+
+	set_ignore_transform_notification(true);
+	reset_to_rest_position();
+	set_ignore_transform_notification(false);
+
+#ifdef TOOLS_ENABLED
+	if (get_gizmo().is_valid())
+		get_gizmo()->redraw();
+#endif
+}
+
+const Transform &PhysicalBone::get_body_offset() const {
+	return body_offset;
+}
+
+void PhysicalBone::set_body_offset(const Transform &p_offset) {
+	body_offset = p_offset;
+	body_offset_inverse = body_offset.affine_inverse();
+
+	_fix_joint_offset();
+
+	set_ignore_transform_notification(true);
+	reset_to_rest_position();
+	set_ignore_transform_notification(false);
+
+#ifdef TOOLS_ENABLED
+	if (get_gizmo().is_valid())
+		get_gizmo()->redraw();
+#endif
+}
+
+const Transform &PhysicalBone::get_joint_offset() const {
+	return joint_offset;
+}
+
+void PhysicalBone::set_static_body(bool p_static) {
+
+	static_body = p_static;
+
+	set_as_toplevel(!static_body);
+
+	_reset_physics_simulation_state();
+}
+
+bool PhysicalBone::is_static_body() {
+	return static_body;
+}
+
+void PhysicalBone::set_simulate_physics(bool p_simulate) {
+	if (simulate_physics == p_simulate) {
+		return;
+	}
+
+	simulate_physics = p_simulate;
+	_reset_physics_simulation_state();
+}
+
+bool PhysicalBone::get_simulate_physics() {
+	return simulate_physics;
+}
+
+bool PhysicalBone::is_simulating_physics() {
+	return _internal_simulate_physics && !_internal_static_body;
+}
+
+void PhysicalBone::set_bone_name(const String &p_name) {
+
+	bone_name = p_name;
+	bone_id = -1;
+
+	update_bone_id();
+	reset_to_rest_position();
+}
+
+const String &PhysicalBone::get_bone_name() const {
+
+	return bone_name;
+}
+
+void PhysicalBone::set_mass(real_t p_mass) {
+
+	ERR_FAIL_COND(p_mass <= 0);
+	mass = p_mass;
+	PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_MASS, mass);
+}
+
+real_t PhysicalBone::get_mass() const {
+
+	return mass;
+}
+
+void PhysicalBone::set_weight(real_t p_weight) {
+
+	set_mass(p_weight / real_t(GLOBAL_DEF("physics/3d/default_gravity", 9.8)));
+}
+
+real_t PhysicalBone::get_weight() const {
+
+	return mass * real_t(GLOBAL_DEF("physics/3d/default_gravity", 9.8));
+}
+
+void PhysicalBone::set_friction(real_t p_friction) {
+
+	ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
+
+	friction = p_friction;
+	PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, friction);
+}
+
+real_t PhysicalBone::get_friction() const {
+
+	return friction;
+}
+
+void PhysicalBone::set_bounce(real_t p_bounce) {
+
+	ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
+
+	bounce = p_bounce;
+	PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, bounce);
+}
+real_t PhysicalBone::get_bounce() const {
+
+	return bounce;
+}
+
+void PhysicalBone::set_gravity_scale(real_t p_gravity_scale) {
+
+	gravity_scale = p_gravity_scale;
+	PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_GRAVITY_SCALE, gravity_scale);
+}
+
+real_t PhysicalBone::get_gravity_scale() const {
+
+	return gravity_scale;
+}
+
+PhysicalBone::PhysicalBone() :
+		PhysicsBody(PhysicsServer::BODY_MODE_STATIC),
+#ifdef TOOLS_ENABLED
+		gizmo_move_joint(false),
+#endif
+		joint_data(NULL),
+		static_body(false),
+		simulate_physics(false),
+		_internal_static_body(!static_body),
+		_internal_simulate_physics(simulate_physics),
+		bone_id(-1),
+		parent_skeleton(NULL),
+		bone_name(""),
+		bounce(0),
+		mass(1),
+		friction(1),
+		gravity_scale(1) {
+
+	set_static_body(static_body);
+	_reset_physics_simulation_state();
+}
+
+PhysicalBone::~PhysicalBone() {
+	memdelete(joint_data);
+}
+
+void PhysicalBone::update_bone_id() {
+	if (!parent_skeleton) {
+		return;
+	}
+
+	const int new_bone_id = parent_skeleton->find_bone(bone_name);
+
+	if (new_bone_id != bone_id) {
+		if (-1 != bone_id) {
+			// Assert the unbind from old node
+			parent_skeleton->unbind_physical_bone_from_bone(bone_id);
+			parent_skeleton->unbind_child_node_from_bone(bone_id, this);
+		}
+
+		bone_id = new_bone_id;
+
+		parent_skeleton->bind_physical_bone_to_bone(bone_id, this);
+
+		_fix_joint_offset();
+		_internal_static_body = !static_body; // Force staticness reset
+		_reset_staticness_state();
+	}
+}
+
+void PhysicalBone::update_offset() {
+#ifdef TOOLS_ENABLED
+	if (parent_skeleton) {
+
+		Transform bone_transform(parent_skeleton->get_global_transform());
+		if (-1 != bone_id)
+			bone_transform *= parent_skeleton->get_bone_global_pose(bone_id);
+
+		if (gizmo_move_joint) {
+			bone_transform *= body_offset;
+			set_joint_offset(bone_transform.affine_inverse() * get_global_transform());
+		} else {
+			set_body_offset(bone_transform.affine_inverse() * get_global_transform());
+		}
+	}
+#endif
+}
+
+void PhysicalBone::reset_to_rest_position() {
+	if (parent_skeleton) {
+		if (-1 == bone_id) {
+			set_global_transform(parent_skeleton->get_global_transform() * body_offset);
+		} else {
+			set_global_transform(parent_skeleton->get_global_transform() * parent_skeleton->get_bone_global_pose(bone_id) * body_offset);
+		}
+	}
+}
+
+void PhysicalBone::_reset_physics_simulation_state() {
+	if (simulate_physics && !static_body) {
+		_start_physics_simulation();
+	} else {
+		_stop_physics_simulation();
+	}
+
+	_reset_staticness_state();
+}
+
+void PhysicalBone::_reset_staticness_state() {
+
+	if (parent_skeleton && -1 != bone_id) {
+		if (static_body && simulate_physics) { // With this check I'm sure the position of this body is updated only when it's necessary
+
+			if (_internal_static_body) {
+				return;
+			}
+
+			parent_skeleton->bind_child_node_to_bone(bone_id, this);
+			_internal_static_body = true;
+		} else {
+
+			if (!_internal_static_body) {
+				return;
+			}
+
+			parent_skeleton->unbind_child_node_from_bone(bone_id, this);
+			_internal_static_body = false;
+		}
+	}
+}
+
+void PhysicalBone::_start_physics_simulation() {
+	if (_internal_simulate_physics || !parent_skeleton) {
+		return;
+	}
+	reset_to_rest_position();
+	PhysicsServer::get_singleton()->body_set_mode(get_rid(), PhysicsServer::BODY_MODE_RIGID);
+	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_force_integration_callback(get_rid(), this, "_direct_state_changed");
+	parent_skeleton->set_bone_ignore_animation(bone_id, true);
+	_internal_simulate_physics = true;
+}
+
+void PhysicalBone::_stop_physics_simulation() {
+	if (!_internal_simulate_physics || !parent_skeleton) {
+		return;
+	}
+	PhysicsServer::get_singleton()->body_set_mode(get_rid(), PhysicsServer::BODY_MODE_STATIC);
+	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_force_integration_callback(get_rid(), NULL, "");
+	parent_skeleton->set_bone_ignore_animation(bone_id, false);
+	_internal_simulate_physics = false;
+}

+ 264 - 0
scene/3d/physics_body.h

@@ -33,6 +33,7 @@
 
 #include "scene/3d/collision_object.h"
 #include "servers/physics_server.h"
+#include "skeleton.h"
 #include "vset.h"
 
 class PhysicsBody : public CollisionObject {
@@ -342,4 +343,267 @@ public:
 	KinematicCollision();
 };
 
+class PhysicalBone : public PhysicsBody {
+
+	GDCLASS(PhysicalBone, PhysicsBody);
+
+public:
+	enum JointType {
+		JOINT_TYPE_NONE,
+		JOINT_TYPE_PIN,
+		JOINT_TYPE_CONE,
+		JOINT_TYPE_HINGE,
+		JOINT_TYPE_SLIDER,
+		JOINT_TYPE_6DOF
+	};
+
+	struct JointData {
+		virtual JointType get_joint_type() { return JOINT_TYPE_NONE; }
+
+		/// "j" is used to set the parameter inside the PhysicsServer
+		virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID());
+		virtual bool _get(const StringName &p_name, Variant &r_ret) const;
+		virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+	};
+
+	struct PinJointData : public JointData {
+		virtual JointType get_joint_type() { return JOINT_TYPE_PIN; }
+
+		virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID());
+		virtual bool _get(const StringName &p_name, Variant &r_ret) const;
+		virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+
+		real_t bias;
+		real_t damping;
+		real_t impulse_clamp;
+
+		PinJointData() :
+				bias(0.3),
+				damping(1.),
+				impulse_clamp(0) {}
+	};
+
+	struct ConeJointData : public JointData {
+		virtual JointType get_joint_type() { return JOINT_TYPE_CONE; }
+
+		virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID());
+		virtual bool _get(const StringName &p_name, Variant &r_ret) const;
+		virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+
+		real_t swing_span;
+		real_t twist_span;
+		real_t bias;
+		real_t softness;
+		real_t relaxation;
+
+		ConeJointData() :
+				swing_span(Math_PI * 0.25),
+				twist_span(Math_PI),
+				bias(0.3),
+				softness(0.8),
+				relaxation(1.) {}
+	};
+
+	struct HingeJointData : public JointData {
+		virtual JointType get_joint_type() { return JOINT_TYPE_HINGE; }
+
+		virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID());
+		virtual bool _get(const StringName &p_name, Variant &r_ret) const;
+		virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+
+		bool angular_limit_enabled;
+		real_t angular_limit_upper;
+		real_t angular_limit_lower;
+		real_t angular_limit_bias;
+		real_t angular_limit_softness;
+		real_t angular_limit_relaxation;
+
+		HingeJointData() :
+				angular_limit_enabled(false),
+				angular_limit_upper(Math_PI * 0.5),
+				angular_limit_lower(-Math_PI * 0.5),
+				angular_limit_bias(0.3),
+				angular_limit_softness(0.9),
+				angular_limit_relaxation(1.) {}
+	};
+
+	struct SliderJointData : public JointData {
+		virtual JointType get_joint_type() { return JOINT_TYPE_SLIDER; }
+
+		virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID());
+		virtual bool _get(const StringName &p_name, Variant &r_ret) const;
+		virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+
+		real_t linear_limit_upper;
+		real_t linear_limit_lower;
+		real_t linear_limit_softness;
+		real_t linear_limit_restitution;
+		real_t linear_limit_damping;
+		real_t angular_limit_upper;
+		real_t angular_limit_lower;
+		real_t angular_limit_softness;
+		real_t angular_limit_restitution;
+		real_t angular_limit_damping;
+
+		SliderJointData() :
+				linear_limit_upper(1.),
+				linear_limit_lower(-1.),
+				linear_limit_softness(1.),
+				linear_limit_restitution(0.7),
+				linear_limit_damping(1.),
+				angular_limit_upper(0),
+				angular_limit_lower(0),
+				angular_limit_softness(1.),
+				angular_limit_restitution(0.7),
+				angular_limit_damping(1.) {}
+	};
+
+	struct SixDOFJointData : public JointData {
+		struct SixDOFAxisData {
+			bool linear_limit_enabled;
+			real_t linear_limit_upper;
+			real_t linear_limit_lower;
+			real_t linear_limit_softness;
+			real_t linear_restitution;
+			real_t linear_damping;
+			bool angular_limit_enabled;
+			real_t angular_limit_upper;
+			real_t angular_limit_lower;
+			real_t angular_limit_softness;
+			real_t angular_restitution;
+			real_t angular_damping;
+			real_t erp;
+
+			SixDOFAxisData() :
+					linear_limit_enabled(true),
+					linear_limit_upper(0),
+					linear_limit_lower(0),
+					linear_limit_softness(0.7),
+					linear_restitution(0.5),
+					linear_damping(1.),
+					angular_limit_enabled(true),
+					angular_limit_upper(0),
+					angular_limit_lower(0),
+					angular_limit_softness(0.5),
+					angular_restitution(0),
+					angular_damping(1.),
+					erp(0.5) {}
+		};
+
+		virtual JointType get_joint_type() { return JOINT_TYPE_6DOF; }
+
+		virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID());
+		virtual bool _get(const StringName &p_name, Variant &r_ret) const;
+		virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+
+		SixDOFAxisData axis_data[3];
+
+		SixDOFJointData() {}
+	};
+
+private:
+#ifdef TOOLS_ENABLED
+	// if false gizmo move body
+	bool gizmo_move_joint;
+#endif
+
+	JointData *joint_data;
+	Transform joint_offset;
+	RID joint;
+
+	Skeleton *parent_skeleton;
+	Transform body_offset;
+	Transform body_offset_inverse;
+	bool static_body;
+	bool _internal_static_body;
+	bool simulate_physics;
+	bool _internal_simulate_physics;
+	int bone_id;
+
+	String bone_name;
+	real_t bounce;
+	real_t mass;
+	real_t friction;
+	real_t gravity_scale;
+
+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;
+	void _notification(int p_what);
+	void _direct_state_changed(Object *p_state);
+
+	static void _bind_methods();
+
+private:
+	static Skeleton *find_skeleton_parent(Node *p_parent);
+	void _fix_joint_offset();
+	void _reload_joint();
+
+public:
+	void _on_bone_parent_changed();
+	void _set_gizmo_move_joint(bool p_move_joint);
+
+public:
+#ifdef TOOLS_ENABLED
+	virtual Transform get_global_gizmo_transform() const;
+	virtual Transform get_local_gizmo_transform() const;
+#endif
+
+	const JointData *get_joint_data() const;
+	Skeleton *find_skeleton_parent();
+
+	int get_bone_id() const { return bone_id; }
+
+	void set_joint_type(JointType p_joint_type);
+	JointType get_joint_type() const;
+
+	void set_joint_offset(const Transform &p_offset);
+	const Transform &get_joint_offset() const;
+
+	void set_body_offset(const Transform &p_offset);
+	const Transform &get_body_offset() const;
+
+	void set_static_body(bool p_static);
+	bool is_static_body();
+
+	void set_simulate_physics(bool p_simulate);
+	bool get_simulate_physics();
+	bool is_simulating_physics();
+
+	void set_bone_name(const String &p_name);
+	const String &get_bone_name() const;
+
+	void set_mass(real_t p_mass);
+	real_t get_mass() const;
+
+	void set_weight(real_t p_weight);
+	real_t get_weight() const;
+
+	void set_friction(real_t p_friction);
+	real_t get_friction() const;
+
+	void set_bounce(real_t p_bounce);
+	real_t get_bounce() const;
+
+	void set_gravity_scale(real_t p_gravity_scale);
+	real_t get_gravity_scale() const;
+
+	PhysicalBone();
+	~PhysicalBone();
+
+private:
+	void update_bone_id();
+	void update_offset();
+	void reset_to_rest_position();
+
+	void _reset_physics_simulation_state();
+	void _reset_staticness_state();
+
+	void _start_physics_simulation();
+	void _stop_physics_simulation();
+};
+
+VARIANT_ENUM_CAST(PhysicalBone::JointType);
+
 #endif // PHYSICS_BODY__H

+ 113 - 0
scene/3d/skeleton.cpp

@@ -33,6 +33,7 @@
 #include "message_queue.h"
 
 #include "core/project_settings.h"
+#include "scene/3d/physics_body.h"
 #include "scene/resources/surface_tool.h"
 
 bool Skeleton::_set(const StringName &p_path, const Variant &p_value) {
@@ -377,6 +378,17 @@ void Skeleton::unparent_bone_and_rest(int p_bone) {
 	_make_dirty();
 }
 
+void Skeleton::set_bone_ignore_animation(int p_bone, bool p_ignore) {
+	ERR_FAIL_INDEX(p_bone, bones.size());
+	bones[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) {
 
 	ERR_FAIL_INDEX(p_bone, bones.size());
@@ -522,6 +534,103 @@ void Skeleton::localize_rests() {
 	}
 }
 
+void _notify_physical_bones_simulation(bool start, Node *p_node) {
+
+	for (int i = p_node->get_child_count() - 1; 0 <= i; --i) {
+		_notify_physical_bones_simulation(start, p_node->get_child(i));
+	}
+
+	PhysicalBone *pb = Object::cast_to<PhysicalBone>(p_node);
+	if (pb) {
+		pb->set_simulate_physics(start);
+	}
+}
+
+void Skeleton::bind_physical_bone_to_bone(int p_bone, PhysicalBone *p_physical_bone) {
+	ERR_FAIL_INDEX(p_bone, bones.size());
+	ERR_FAIL_COND(bones[p_bone].physical_bone);
+	ERR_FAIL_COND(!p_physical_bone);
+	bones[p_bone].physical_bone = p_physical_bone;
+
+	_rebuild_physical_bones_cache();
+}
+
+void Skeleton::unbind_physical_bone_from_bone(int p_bone) {
+	ERR_FAIL_INDEX(p_bone, bones.size());
+	bones[p_bone].physical_bone = NULL;
+
+	_rebuild_physical_bones_cache();
+}
+
+PhysicalBone *Skeleton::get_physical_bone(int p_bone) {
+	ERR_FAIL_INDEX_V(p_bone, bones.size(), NULL);
+
+	return bones[p_bone].physical_bone;
+}
+
+PhysicalBone *Skeleton::get_physical_bone_parent(int p_bone) {
+	ERR_FAIL_INDEX_V(p_bone, bones.size(), NULL);
+
+	if (bones[p_bone].cache_parent_physical_bone) {
+		return bones[p_bone].cache_parent_physical_bone;
+	}
+
+	return _get_physical_bone_parent(p_bone);
+}
+
+PhysicalBone *Skeleton::_get_physical_bone_parent(int p_bone) {
+	ERR_FAIL_INDEX_V(p_bone, bones.size(), NULL);
+
+	const int parent_bone = bones[p_bone].parent;
+	if (0 > parent_bone) {
+		return NULL;
+	}
+
+	PhysicalBone *pb = bones[parent_bone].physical_bone;
+	if (pb) {
+		return pb;
+	} else {
+		return get_physical_bone_parent(parent_bone);
+	}
+}
+
+void Skeleton::_rebuild_physical_bones_cache() {
+	const int b_size = bones.size();
+	for (int i = 0; i < b_size; ++i) {
+		bones[i].cache_parent_physical_bone = _get_physical_bone_parent(i);
+		if (bones[i].physical_bone)
+			bones[i].physical_bone->_on_bone_parent_changed();
+	}
+}
+
+void Skeleton::physical_bones_simulation(bool start) {
+	_notify_physical_bones_simulation(start, this);
+}
+
+void _physical_bones_add_remove_collision_exception(bool p_add, Node *p_node, RID p_exception) {
+
+	for (int i = p_node->get_child_count() - 1; 0 <= i; --i) {
+		_physical_bones_add_remove_collision_exception(p_add, p_node->get_child(i), p_exception);
+	}
+
+	CollisionObject *co = Object::cast_to<CollisionObject>(p_node);
+	if (co) {
+		if (p_add) {
+			PhysicsServer::get_singleton()->body_add_collision_exception(co->get_rid(), p_exception);
+		} else {
+			PhysicsServer::get_singleton()->body_remove_collision_exception(co->get_rid(), p_exception);
+		}
+	}
+}
+
+void Skeleton::physical_bones_add_collision_exception(RID p_exception) {
+	_physical_bones_add_remove_collision_exception(true, this, p_exception);
+}
+
+void Skeleton::physical_bones_remove_collision_exception(RID p_exception) {
+	_physical_bones_add_remove_collision_exception(false, this, p_exception);
+}
+
 void Skeleton::_bind_methods() {
 
 	ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton::add_bone);
@@ -558,6 +667,10 @@ void Skeleton::_bind_methods() {
 
 	ClassDB::bind_method(D_METHOD("get_bone_transform", "bone_idx"), &Skeleton::get_bone_transform);
 
+	ClassDB::bind_method(D_METHOD("physical_bones_simulation", "start"), &Skeleton::physical_bones_simulation);
+	ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &Skeleton::physical_bones_add_collision_exception);
+	ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &Skeleton::physical_bones_remove_collision_exception);
+
 	BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON);
 }
 

+ 32 - 0
scene/3d/skeleton.h

@@ -37,6 +37,8 @@
 /**
 	@author Juan Linietsky <[email protected]>
 */
+
+class PhysicalBone;
 class Skeleton : public Spatial {
 
 	GDCLASS(Skeleton, Spatial);
@@ -48,6 +50,8 @@ class Skeleton : public Spatial {
 		bool enabled;
 		int parent;
 
+		bool ignore_animation;
+
 		bool disable_rest;
 		Transform rest;
 		Transform rest_global_inverse;
@@ -60,13 +64,19 @@ class Skeleton : public Spatial {
 
 		Transform transform_final;
 
+		PhysicalBone *physical_bone;
+		PhysicalBone *cache_parent_physical_bone;
+
 		List<uint32_t> nodes_bound;
 
 		Bone() {
 			parent = -1;
 			enabled = true;
+			ignore_animation = false;
 			custom_pose_enable = false;
 			disable_rest = false;
+			physical_bone = NULL;
+			cache_parent_physical_bone = NULL;
 		}
 	};
 
@@ -118,6 +128,9 @@ public:
 
 	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);
 	bool is_bone_rest_disabled(int p_bone) const;
 
@@ -149,6 +162,25 @@ public:
 
 	void localize_rests(); // used for loaders and tools
 
+	// Physical bone API
+
+	void bind_physical_bone_to_bone(int p_bone, PhysicalBone *p_physical_bone);
+	void unbind_physical_bone_from_bone(int p_bone);
+
+	PhysicalBone *get_physical_bone(int p_bone);
+	PhysicalBone *get_physical_bone_parent(int p_bone);
+
+private:
+	/// This is a slow API os it's cached
+	PhysicalBone *_get_physical_bone_parent(int p_bone);
+	void _rebuild_physical_bones_cache();
+
+public:
+	void physical_bones_simulation(bool start);
+	void physical_bones_add_collision_exception(RID p_exception);
+	void physical_bones_remove_collision_exception(RID p_exception);
+
+public:
 	Skeleton();
 	~Skeleton();
 };

+ 10 - 0
scene/3d/spatial.cpp

@@ -286,6 +286,16 @@ Transform Spatial::get_global_transform() const {
 	return data.global_transform;
 }
 
+#ifdef TOOLS_ENABLED
+Transform Spatial::get_global_gizmo_transform() const {
+	return get_global_transform();
+}
+
+Transform Spatial::get_local_gizmo_transform() const {
+	return get_transform();
+}
+#endif
+
 Spatial *Spatial::get_parent_spatial() const {
 
 	return data.parent;

+ 5 - 0
scene/3d/spatial.h

@@ -145,6 +145,11 @@ public:
 	Transform get_transform() const;
 	Transform get_global_transform() const;
 
+#ifdef TOOLS_ENABLED
+	virtual Transform get_global_gizmo_transform() const;
+	virtual Transform get_local_gizmo_transform() const;
+#endif
+
 	void set_as_toplevel(bool p_enabled);
 	bool is_set_as_toplevel() const;
 

+ 8 - 9
scene/animation/animation_player.cpp

@@ -246,8 +246,9 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim) {
 
 		if (a->track_get_path(i).get_subname_count() == 1 && Object::cast_to<Skeleton>(child)) {
 
-			bone_idx = Object::cast_to<Skeleton>(child)->find_bone(a->track_get_path(i).get_subname(0));
-			if (bone_idx == -1) {
+			Skeleton *sk = Object::cast_to<Skeleton>(child);
+			bone_idx = sk->find_bone(a->track_get_path(i).get_subname(0));
+			if (bone_idx == -1 || sk->is_bone_ignore_animation(bone_idx)) {
 
 				continue;
 			}
@@ -579,16 +580,14 @@ void AnimationPlayer::_animation_process2(float p_delta) {
 }
 
 void AnimationPlayer::_animation_update_transforms() {
+	{
+		Transform t;
+		for (int i = 0; i < cache_update_size; i++) {
 
-	for (int i = 0; i < cache_update_size; i++) {
+			TrackNodeCache *nc = cache_update[i];
 
-		TrackNodeCache *nc = cache_update[i];
+			ERR_CONTINUE(nc->accum_pass != accum_pass);
 
-		ERR_CONTINUE(nc->accum_pass != accum_pass);
-
-		if (nc->spatial) {
-
-			Transform t;
 			t.origin = nc->loc_accum;
 			t.basis.set_quat_scale(nc->rot_accum, nc->scale_accum);
 			if (nc->skeleton && nc->bone_idx >= 0) {

+ 7 - 1
scene/animation/animation_tree_player.cpp

@@ -812,6 +812,12 @@ void AnimationTreePlayer::_process_animation(float p_delta) {
 
 		t.value = t.object->get_indexed(t.subpath);
 		t.value.zero();
+
+		if (t.skeleton) {
+			t.skip = t.skeleton->is_bone_ignore_animation(t.bone_idx);
+		} else {
+			t.skip = false;
+		}
 	}
 
 	/* STEP 2 PROCESS ANIMATIONS */
@@ -884,7 +890,7 @@ void AnimationTreePlayer::_process_animation(float p_delta) {
 
 		Track &t = E->get();
 
-		if (!t.object)
+		if (t.skip || !t.object)
 			continue;
 
 		if (t.subpath.size()) { // value track

+ 2 - 0
scene/animation/animation_tree_player.h

@@ -107,6 +107,8 @@ private:
 		Vector3 scale;
 
 		Variant value;
+
+		bool skip;
 	};
 
 	typedef Map<TrackKey, Track> TrackMap;

+ 1 - 0
scene/register_scene_types.cpp

@@ -390,6 +390,7 @@ void register_scene_types() {
 	ClassDB::register_class<RigidBody>();
 	ClassDB::register_class<KinematicCollision>();
 	ClassDB::register_class<KinematicBody>();
+	ClassDB::register_class<PhysicalBone>();
 
 	ClassDB::register_class<VehicleBody>();
 	ClassDB::register_class<VehicleWheel>();