| 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 | |
| 38 | class 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 | |
| 80 | public: |
| 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 | |
| 100 | private: |
| 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 | |
| 109 | public: |
| 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 | |
| 120 | class 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 | |
| 139 | protected: |
| 140 | void _validate_property(PropertyInfo &p_property) const; |
| 141 | |
| 142 | static void _bind_methods(); |
| 143 | virtual void _notification(int p_what); |
| 144 | |
| 145 | public: |
| 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 | |
| 186 | private: |
| 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 | |