1/**************************************************************************/
2/* skeleton_ik_3d.h */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#ifndef SKELETON_IK_3D_H
32#define SKELETON_IK_3D_H
33
34#ifndef _3D_DISABLED
35
36#include "scene/3d/skeleton_3d.h"
37
38class FabrikInverseKinematic {
39 struct EndEffector {
40 BoneId tip_bone;
41 Transform3D goal_transform;
42 };
43
44 struct ChainItem {
45 Vector<ChainItem> children;
46 ChainItem *parent_item = nullptr;
47
48 // Bone info
49 BoneId bone = -1;
50
51 real_t length = 0.0;
52 /// Positions relative to root bone
53 Transform3D initial_transform;
54 Vector3 current_pos;
55 // Direction from this bone to child
56 Vector3 current_ori;
57
58 ChainItem *find_child(const BoneId p_bone_id);
59 ChainItem *add_child(const BoneId p_bone_id);
60 };
61
62 struct ChainTip {
63 ChainItem *chain_item = nullptr;
64 const EndEffector *end_effector = nullptr;
65
66 ChainTip() {}
67
68 ChainTip(ChainItem *p_chain_item, const EndEffector *p_end_effector) :
69 chain_item(p_chain_item),
70 end_effector(p_end_effector) {}
71 };
72
73 struct Chain {
74 ChainItem chain_root;
75 ChainItem *middle_chain_item = nullptr;
76 Vector<ChainTip> tips;
77 Vector3 magnet_position;
78 };
79
80public:
81 struct Task {
82 RID self;
83 Skeleton3D *skeleton = nullptr;
84
85 Chain chain;
86
87 // Settings
88 real_t min_distance = 0.01;
89 int max_iterations = 10;
90
91 // Bone data
92 BoneId root_bone = -1;
93 Vector<EndEffector> end_effectors;
94
95 Transform3D goal_global_transform;
96
97 Task() {}
98 };
99
100private:
101 /// Init a chain that starts from the root to tip
102 static bool build_chain(Task *p_task, bool p_force_simple_chain = true);
103
104 static void solve_simple(Task *p_task, bool p_solve_magnet, Vector3 p_origin_pos);
105 /// Special solvers that solve only chains with one end effector
106 static void solve_simple_backwards(const Chain &r_chain, bool p_solve_magnet);
107 static void solve_simple_forwards(Chain &r_chain, bool p_solve_magnet, Vector3 p_origin_pos);
108
109public:
110 static Task *create_simple_task(Skeleton3D *p_sk, BoneId root_bone, BoneId tip_bone, const Transform3D &goal_transform);
111 static void free_task(Task *p_task);
112 // The goal of chain should be always in local space
113 static void set_goal(Task *p_task, const Transform3D &p_goal);
114 static void make_goal(Task *p_task, const Transform3D &p_inverse_transf, real_t blending_delta);
115 static void solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position);
116
117 static void _update_chain(const Skeleton3D *p_skeleton, ChainItem *p_chain_item);
118};
119
120class SkeletonIK3D : public Node {
121 GDCLASS(SkeletonIK3D, Node);
122
123 StringName root_bone;
124 StringName tip_bone;
125 real_t interpolation = 1.0;
126 Transform3D target;
127 NodePath target_node_path_override;
128 bool override_tip_basis = true;
129 bool use_magnet = false;
130 Vector3 magnet_position;
131
132 real_t min_distance = 0.01;
133 int max_iterations = 10;
134
135 Variant skeleton_ref = Variant();
136 Variant target_node_override_ref = Variant();
137 FabrikInverseKinematic::Task *task = nullptr;
138
139protected:
140 void _validate_property(PropertyInfo &p_property) const;
141
142 static void _bind_methods();
143 virtual void _notification(int p_what);
144
145public:
146 SkeletonIK3D();
147 virtual ~SkeletonIK3D();
148
149 void set_root_bone(const StringName &p_root_bone);
150 StringName get_root_bone() const;
151
152 void set_tip_bone(const StringName &p_tip_bone);
153 StringName get_tip_bone() const;
154
155 void set_interpolation(real_t p_interpolation);
156 real_t get_interpolation() const;
157
158 void set_target_transform(const Transform3D &p_target);
159 const Transform3D &get_target_transform() const;
160
161 void set_target_node(const NodePath &p_node);
162 NodePath get_target_node();
163
164 void set_override_tip_basis(bool p_override);
165 bool is_override_tip_basis() const;
166
167 void set_use_magnet(bool p_use);
168 bool is_using_magnet() const;
169
170 void set_magnet_position(const Vector3 &p_local_position);
171 const Vector3 &get_magnet_position() const;
172
173 void set_min_distance(real_t p_min_distance);
174 real_t get_min_distance() const { return min_distance; }
175
176 void set_max_iterations(int p_iterations);
177 int get_max_iterations() const { return max_iterations; }
178
179 Skeleton3D *get_parent_skeleton() const;
180
181 bool is_running();
182
183 void start(bool p_one_time = false);
184 void stop();
185
186private:
187 Transform3D _get_target_transform();
188 void reload_chain();
189 void reload_goal();
190 void _solve_chain();
191};
192
193#endif // _3D_DISABLED
194
195#endif // SKELETON_IK_3D_H
196